Socket Tutorial

From ElphelWiki
Jump to: navigation, search

Socket Communication Tutorial with Elphel Cameras

This Tutorial will guide through creating a client - server communication framework example between a host computer and an Elphel camera using C.

I will not explain what every single line of source code does but rather how everything acts together.

This will only work under Linux!

Installing the Axis Compiler

The camera runs a kind of embedded Linux so we cannot use the normal (gcc) compiler for running our code on the camera but need a compiler for a specific target running environment.

Axis has a very comprehensive guide on installing and using their compiler.

Downloading and installing the compiler: http://developer.axis.com/wiki/doku.php?id=axis:compiler_install

This compile can take quite some time so make yourself a cup of tea or two.

The second guide on the Axis wiki is a short compile walkthrough: http://developer.axis.com/wiki/doku.php?id=axis:compiling_for_cris_howto, which we will cover later for our server source as well anyway.

Creating the Client Application

When referring to a client here I mean the computer which we will use to send requests to the camera.

Lets create a new file called client.c and paste the following code into it (thanks to the original creator):

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>

#define BUFFSIZE 32
void Die(char *mess) { perror(mess); exit(1); }


int main(int argc, char *argv[]) {
	int sock;
	struct sockaddr_in echoserver;
	char buffer[BUFFSIZE];
	unsigned int echolen;
	int received = 0;

	if (argc != 4) {
		fprintf(stderr, "USAGE: TCPecho <server_ip> <word> <port>\n");
		exit(1);
	}
	/* Create the TCP socket */
	if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) 	
	{
		Die("Failed to create socket");
	}

	/* Construct the server sockaddr_in structure */
	memset(&echoserver, 0, sizeof(echoserver));       /* Clear struct */
	echoserver.sin_family = AF_INET;                  /* Internet/IP */
	echoserver.sin_addr.s_addr = inet_addr(argv[1]);  /* IP address */
	echoserver.sin_port = htons(atoi(argv[3]));       /* server port */
	/* Establish connection */
	if (connect(sock, (struct sockaddr *) &echoserver, sizeof(echoserver)) < 0) {
		Die("Failed to connect with server");
	}

	/* Send the word to the server */
	echolen = strlen(argv[2]);
	if (send(sock, argv[2], echolen, 0) != echolen) {
		Die("Mismatch in number of sent bytes");
	}
	/* Receive the word back from the server */
	fprintf(stdout, "Received: ");
	while (received < echolen) {
		int bytes = 0;
		if ((bytes = recv(sock, buffer, BUFFSIZE-1, 0)) < 1) {
			Die("Failed to receive bytes from server");
		}
		received += bytes;
		buffer[bytes] = '\0';        /* Assure null terminated string */
		fprintf(stdout, buffer);
	}

	fprintf(stdout, "\n");
	close(sock);
	exit(0);
}

The next step requires a working compile environment. Make sure you have "build-essentials" package installed or otherwise get it with (typing in terminal):

sudo apt-get install build-essentials

Now we compile this the good old linux way by opening a terminal.

gcc client.c -o client.o
gcc is the compiler
client.c is the source file we just created
-o client.o defines where and what we want the compiled output to be

If everything goes well there should now be a file called client.o in the same directory as the source file. If it did not work the compiler should have supplied one or more error messages.

To test the application we just compile now type:

./client.o

You should get an output like this:

USAGE: TCPecho <server_ip> <word> <port>

More about the usage later.

Creating the Server Application

Create another file called server.c and paste the following source code into it:

#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>

#define MAXPENDING 5    /* Max connection requests */
#define BUFFSIZE 32
void Die(char *mess) { perror(mess); exit(1); }


void HandleClient(int sock) {
	char buffer[BUFFSIZE];
	int received = -1;
	/* Receive message */
	if ((received = recv(sock, buffer, BUFFSIZE, 0)) < 0) {
		Die("Failed to receive initial bytes from client");
	}
	/* Send bytes and check for more incoming data in loop */
	while (received > 0) {
		/* Send back received data */
		if (send(sock, buffer, received, 0) != received) {
			Die("Failed to send bytes to client");
		}
		/* Check for more data */
		if ((received = recv(sock, buffer, BUFFSIZE, 0)) < 0) {
			Die("Failed to receive additional bytes from client");
		}
	}
	close(sock);
}

int main(int argc, char *argv[]) {
	int serversock, clientsock;
	struct sockaddr_in echoserver, echoclient;

	if (argc != 2) {
		fprintf(stderr, "USAGE: echoserver <port>\n");
		exit(1);
	}
	/* Create the TCP socket */
	if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
		Die("Failed to create socket");
	}
	/* Construct the server sockaddr_in structure */
	memset(&echoserver, 0, sizeof(echoserver));       /* Clear struct */
	echoserver.sin_family = AF_INET;                  /* Internet/IP */
	echoserver.sin_addr.s_addr = htonl(INADDR_ANY);   /* Incoming addr */
	echoserver.sin_port = htons(atoi(argv[1]));       /* server port */

	/* Bind the server socket */
	if (bind(serversock, (struct sockaddr *) &echoserver, sizeof(echoserver)) < 0) {
		Die("Failed to bind the server socket");
	}
	/* Listen on the server socket */
	if (listen(serversock, MAXPENDING) < 0) {
		Die("Failed to listen on server socket");
	}
	/* Run until cancelled */
	while (1) {
		unsigned int clientlen = sizeof(echoclient);
		/* Wait for client connection */
		if ((clientsock = accept(serversock, (struct sockaddr *) &echoclient, &clientlen)) < 0) 
		{
			Die("Failed to accept client connection");
		}
		fprintf(stdout, "Client connected: %s\n", inet_ntoa(echoclient.sin_addr));
		HandleClient(clientsock);
	}
}

Now we need to compile the server as well but with the compiler we installed in step 1.

In installed the compiler to the default location at: /usr/local/cris/ so the command to compile the server is (for me):

/usr/local/cris/bin/crisv32-axis-linux-gnu-gcc server.c -o server.o

We need to use the v32 version of the compiler because the latest Elphel cameras use the new Etrax FS Board.

Well well, now we should have everything together to make our first test.

Testing

Use your favorite ftp client to connect to the Elphel camera and upload the server.o to a directory of your choice.

Then use an ssh client like putty to connect to the camera.

Go to the folder where your server.o now resides and start it by typing:

./server.o 1234
1234 here is the port on which the server will listen for messages, this can be any port of choice that is not yet already in use by a different service.

The server should now run until terminated so just leave the window open.

On your client computer now start the client.o by typing:

./client.o *camera_ip* *message* 1234
*camera_ip* is - surprise - the IP of your camera (192.168.0.9 by default)
*message* is whatever you like (use quotation marks if you want your message to be multiple words long, like "hello world")
1234 is the port we told the server to listen to

I sent "hello world" by typing:

./client.o 192.168.0.9 "hello world" 1234

and got an echo back from the camera:

Received: hello world

The server at the same time outputs:

Client connected: 192.168.0.100 

Which is the IP of the host computer I sent the message from.

So this simple example code we used on the server just echoes back the message the client sent. But it could send all kind of information to the client on certain request messages.

So now it's a matter of your creativity what kind of services this method can be used to create.