Home > C > Echo server in C

Echo server in C


In order to create an echo server we must introduce the concept of socket in C.

Sockets can be seen as an endpoint for cummunication, and in C are created by including in our code the following libraries:

<sys/types.h>
<sys/socket.h>

and by using the function int socket(int domain, int type, int protocol)

Let’s explain briefly the three variables used in the previous function:

int domain represents a communication domain, the protocol family to be used for communication (common values are AF_UNIX for local communication, AF_INET for IPv4 Internet protocols and AF_INET6 for IPv6).

int type specifies the communication semantics (commonly we use SOCK_STREAM that provides sequenced, reliable, two-way, connection-based byte streams but can be used other types as SOCK_RAW which provides raw network protocol access).

int protocol indicates a particular protocol for the socket, normally only a single protocol exists for supporting a particular socket type in a family but in some cases there can be more than one and this variable specifies which one should be used.

Function int socket(…) returns a socket descriptor if valid, otherwise it returns -1 and a errno to handle the error. To send or receive data (e.g to/from another process or machine) the socket must be connected to another socket.

Since we want to create a server first, we must introduce a way to setup a port to listen to. We do this by using the function int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen) (the same libraries as before must have included). This function assigns the address specified by variable addr to the socket assigned to socket descriptor sockfd. addrlen specifies the size in bytes of the address structure pointed to by addr.

The struct sockaddr is structure representing an IP socket address, thus a pain (IP, PORT). This structure may change based on protocol you use, but is something like:

struct sockaddr {
	short sin_family;         // e.g. AF_INET for IPv4
	unsigned short sin_port;  // e.g. htons(4320) see htons()
	struct in_addr sin_addr;  // structure containing the IP address
	char sin_zero[8];
};

where:

in_addr {
	unsigned long s_addr;
};

If successful, bind() returns 0; otherwise it returns -1 and sets errno to indicate the error.
After binding, listening operation can be started as: listen(int sockefd, in queue) where socketfd is our socket descriptor and queue is the maximum number of connections pending. If successful, listen() returns 0; otherwise it returns -1 and sets errno to indicate the error.

To accept a connection we use the function int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) which is used with connection based sockets such as streams. Argument sockfd is a socket that has been created with socket(), bound to a local address with bind(), and is listening for connections after a listen(). The argument addr is a pointer to a sockaddr structure filled in with the address of the peer socket, as known to the communications layer. The addrlen argument is a value-result argument: the caller must ini tialize it to contain the size (in bytes) of the structure pointed to by addr; on return it will contain the actual size of the peer address. If an error occurs, -1 is returned and errno is set to indicate the cause.

For exchanging data we use two functions: ssize_t send(int sockfd, const void *buf, size_t len, int flags) where buf is the message you want to send, flags is usually set to 0 and the function ssize_t recv(int sockfd, void *buf, size_t len, int flags). These functions return -1 in case of an error.

Finally to close a connection we include library and call int close(int sockfd)

Here an example of a server that waits for a connection and greets the user connected. (note that I did not check the return value of the functions):

#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>

#define MAX_CONNECTIONS    (1000)
#define MAX_LINE           (1000)

int main()
{
  int sockfd;
  sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

  struct sockaddr_in my_addr;
  my_addr.sin_family = AF_INET;
  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  my_addr.sin_port = htons(4000);

  bind(sockfd, (struct sockaddr *) &my_addr, sizeof(my_addr));
  listen(sockfd, MAX_CONNECTIONS);

  while(1)
    {
      struct sockaddr_in client_addr;
      int client_len = sizeof(client_addr);

      int client_fd = accept(sockfd, (struct sockaddr *) &client_addr,  &client_len);

      char buf[MAX_LINE];
      strcpy(buf, "Hello user!\n");
      send(client_fd, buf, strlen(buf) + 1, 0);
      close(client_fd);
    }
}

To run the server type in one shell the following:

bellerofonte@pegaso:~$ ./echoserv

and in another:

bellerofonte@pegaso:~$ telnet localhost 4000
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Hello user!
Connection closed by foreign host.

Note that this server accepts only one connection (I will post new code for a multi-client server soon)!

Categories: C Tags: , ,
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: