C Socket Tutorial – Echo Server

Following my previous post here , I’ll get to the programming part.But before that a few concepts that are important.

Client Server Model

Anytime two devices want to connect through a TCP/IP socket , one of them has to be a client and the other has to be a server. As an example your computer might be the client and Google computer might be the server. The client should be aware of the Server’s IP Address and Port Number, while the server can accept any client and determine his IP later.

File Descriptors, not just for files

As the name suggests file descriptors hold information about a file, but not just files, Adhering to Unix’s philosophy of everything is a file, all I/O is done through file descriptors from the programmer’s side. This can include access to USB ports, Hard Disks , Optical Drives , Cameras and most importantly Network Connections. What this means is , no matter which of these you access, programmer can assume that they are files and live in blissful oblivion. The actual work on the other hand is done by , the Programmer’s true friend, the Silent Guardian , the Operating System.

In Linux file-descriptors are accessed with their corresponding integers. Three of them are reserved by default for special files.

0 – Standard Input – Analogous to the  keyboard, whatever the user types from they keyboard can be read from this file descriptor.

1 – Standard Output- Whatever you write to this file descriptor , appears as text on the console.

2 – Standard Error – To display error messages.

The above three also come into play while dealing with pipes. For a more detailed reference , go to Wikipedia.

A simple program to illustrate file descriptors.

Hello File Descriptors


/* Not stdio.h */
#include <unistd.h>

void main()
{
	char str[100];

	/*Write on screen*/
	write(1,"\nType Something : ",17);

	/*Read at most 100 chars from console into 'str' */
	read(0,str,100);

	write(1,"\nYou typed :\n",13);

	/*write 10 chars from 'str' to screen */
	write(1,str,10);

	/*newline*/
	write(1,"\n",1);
}

Output – For the lazy ones


Type Something :hi how are you

You typed :
hi how are

read/write


write(int fd, const void *buf, size_t n);

fd – File Descriptor to write to
buf – Pointer to Data to be written
n – Number of bytes to write
returns – Number of bytes written, negative if error


read(int fd, const void *buf, size_t n);

fd – File Descriptor to read from
buf – Pointer to where data read should be stored
n – Maximum number of bytes to be read.
returns – Number of bytes read, can be less than n if there is end of input , negative if error

Lets get to handling sockets now.

Hello Sockets – Echo Server

Echo Server is a device which sends back whatever data it has received
I have put comments in the code to explain what each function does. Apart from that I recommend you Google each of them to know their prototype , as I won’t be mentioning them , or you can use man in Linux.

/*Required Headers*/

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>

int main()
{

	char str[100];
	int listen_fd, comm_fd;

	struct sockaddr_in servaddr;

	listen_fd = socket(AF_INET, SOCK_STREAM, 0);

	bzero( &servaddr, sizeof(servaddr));

	servaddr.sin_family = AF_INET;
	servaddr.sin_addr.s_addr = htons(INADDR_ANY);
	servaddr.sin_port = htons(22000);

	bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

	listen(listen_fd, 10);

	comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);

	while(1)
	{

		bzero( str, 100);

		read(comm_fd,str,100);

		printf("Echoing back - %s",str);

		write(comm_fd, str, strlen(str)+1);

	}
}

Line by Line

int listen_fd, comm_fd;

File Descriptors to be used

struct sockaddr_in servaddr;

Struct to hold IP Address and Port Numbers

listen_fd = socket(AF_INET, SOCK_STREAM, 0);

Each server needs to “listen” for connections. The above function creates a socket with AF_INET ( IP Addressing ) and of type SOCK_STREAM. Data from all devices wishing to connect on this socket will be redirected to listen_fd.

bzero( &servaddr, sizeof(servaddr));

Clear servaddr ( Mandatory ).

servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htons(INADDR_ANY);
servaddr.sin_port = htons(22000);
  • Set Addressing scheme to – AF_INET ( IP )
  • Allow any IP to connect – htons(INADDR_ANY)
  • Listen on port 22000 – htons(22000)

[ Remember how Google listened on Port 80 ? ]

bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));

Prepare to listen for connections from address/port specified in sockaddr ( Any IP on port 22000 ).

listen(listen_fd, 10);

Start Listening for connections , keep at the most 10 connection requests waiting.If there are more than 10 computers wanting to connect at a time, the 11th one fails to.

comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);

Accept a connection from any device who is willing to connect, If there is no one who wants to connect , wait. A file descriptor is returned. This can finally be used to communicate , whatever is sent by the device accepted can be read from comm_fd, whatever is written to comm_fd is sent to the other device.

	bzero( str, 100);
	read(comm_fd,str,100);
	printf("Echoing back - %s",str);
	write(comm_fd, str, strlen(str)+1);

The Server then does the above forever

  • Clear str
  • read at most 100 bytes into str
  • display what it reads
  • send back str , (length of string + 1) so that is sent as well

Hello Sockets – Client


#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include<string.h>

int main(int argc,char **argv)
{
	int sockfd,n;
	char sendline[100];
	char recvline[100];
	struct sockaddr_in servaddr;

	sockfd=socket(AF_INET,SOCK_STREAM,0);
	bzero(&servaddr,sizeof servaddr);

	servaddr.sin_family=AF_INET;
	servaddr.sin_port=htons(22000);

	inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));

	connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

	while(1)
	{
		bzero( sendline, 100);
		bzero( recvline, 100);
        fgets(sendline,100,stdin); /*stdin = 0 , for standard input */

        write(sockfd,sendline,strlen(sendline)+1);
        read(sockfd,recvline,100);
        printf("%s",recvline);
	}

}

Line by Line

I’ll only go over the new parts

inet_pton(AF_INET,"127.0.0.1",&(servaddr.sin_addr));

Set IP address in servaddr to “127.0.0.1” ( computers way of saying myself ) since our server is also on the same machine . The address in servaddr needs to be in integer format , hence the function inet_pton

connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));

Connect to the device whose address and port number is specified in servaddr.

bzero( sendline, 100);
bzero( recvline, 100);
fgets(sendline,100,stdin); /*stdin = 0 , for standard input */
write(sockfd,sendline,strlen(sendline)+1);
read(sockfd,recvline,100);
printf("%s",recvline);

The Client then does following forever.

  • Clear sendline and recvline
  • read string from stdin in sendline ( stdin is 0 for standard input )
  • write sendline in sockfd
  • read from sockfd in srecvline
  • Display recvline

Output – Client/Server in a not so pleasant Conversation


Hi Server
Hi Server
How are you ?
How are you ?
Stop imitating me !!!
Stop imitating me !!!
STAAAAHHPPPPPPPP !
STAAAAHHPPPPPPPP !

I hope this post is of some help to whoever reads it. I tried to keep it as simple as possible . Leave Comments

Advertisements

34 thoughts on “C Socket Tutorial – Echo Server

  1. Sir, I don’t understand how to run both these programs at the same time. I mean, to make the server respond to the client, both server and client should be running. How to do that?

  2. hi vighnesh,

    Can you tell me how much resources are used in server side to process a simple client request.
    Pls tell me in terms of memory and cpu usage.

    Thanks
    Pradeep

    • Hello
      It is tough to know exactly unless you really dig into the kernel. But you can run the “top” command to view memory usage in real time.

      Inside the program, the character arrays declared in the beginning is about the only thing that can be changed.

      Thanks
      Vighnesh

  3. its really funny and straightforward…thanks for this could u please tell me a way..to execute these programs in my pc.

  4. Hello,

    How would I apply a username/password system to this set up ? I would like the server to require the connection to verify a username/password before allowing information to be sent.

    Thanks!

    • You can have the server request a username/password (by doing a write with a certain message) and then reading the username/password via a read. You could store the valid names/passwords in a file. But unless you do some encryption/decryption, all data transmitted and received will be insecure (anyone could see it by sniffing network packets).

  5. “write(sockfd,sendline,strlen(sendline)+1);”

    Why are you adding +1 to the strlen(sendline)?
    For me, it also works if I don’t do that…

    Thanks!

  6. is it possible to call the client code with different parameters from within the server code? I mean is it possible to first make a request to server for some processing and then server to call another client code to do some other stuff, for example open a file to search a string? Thank you.

    • I am not sure what you are trying to achieve. When you say “call another client code” do you mean that you want to make another client do something, or you want the server itself do to something by using the code from the client ?

      • First of all, thanks for your response. Actually I have to write a client server program where the client program calls a function search(names, groupname) and the server has the information about the path of the file (located on some different machine) related to the particular group name. I am using GDBM database where groupname is key and the name of the file path as the data. If the groupname is found in the database then server will have to open the file to look for the name. Here I am very much confused as how the server will make request, as client(may be) to open any file which is not on the same machine or on the same server. Even, I have no idea where I should define the search function with the two parameters as search(name, TextFile) . Finally, the server will return success or failure message to the client. In short, I have to design a code where the client requests to search the names in the file related to particular group where the group and its related files are located in DB on Server. I would grateful to you for any kind of help.Thank you.

      • So what I gather from your description is that the server has some information which the client needs and upon the client’s request the server will search and retrieve it. I don’t know in what context you want to do this, but a naive solution might be that you define your own message format. The client can send its request to the server over a socket, where the search parameters are strings separated by some special character. The server receives this message, unpacks the parameters, executes the required DB operation and sends the result back. In short, the client tells the server what to do via a socket, and the server executes the required operation (possibly over a database) and sends the result back.

  7. Hi!
    Interesting article for the beginners (like me) in C socket programming!
    My question is on the first line of the the server code: “char str[100];”
    Does this really mean that the input from the client cannot be longer than 100 characters in size? If so, what if the client is to send bigger chunks of data (like in our case, MBs of binary data)? how should that be implemented?

    Cheers
    Ramin

    • Yes that is correct. If you have megabytes worth of data you want to receive you can read it in small chunks of 100 (or maybe 1000) and then store it in another data structure or write it to a file.

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