Socket Programming using TCP/IP

Socket programs are used to communicate between various processes usually running on different systems. It is mostly used to create a client-server environment. This post  provides the various functions used to create the server and client program and an example program. In the example, the client program sends a file name to the server and the server sends the contents of the file back to the client.

Functions used in server program:

  • socket() –    This call creates an unnamed socket and returns a file descriptor to the calling process.
    usage :   int socket(int domain, int type, int protocol)
    Eg:  sockfd=socket(AF_INET,SOCK_STREAM,0);
    here AF_INET means the the communication is over the internet domain.SOCL_STREAM indicates its a stream type of communication and 0 indicates the protocol used is TCP/IP.
  • bzero()–    this call is used set all the values of the buffer to zero.
    usage: bzero(pointer_to_buffer,size_of_buffer)
    eg:  bzero((char *)&serv_addr,sizeof(serv_addr));
    here serv_addr is of the struct type sockaddr_in which has members used to describe the complete address of a system.
  • serv_addr.sin_family=AF_INET;
    As stated earlier the serv_addr has severaal members and the first of it is sin_family and it contains the code for the address family  and is always AF_INET, indicating the internet domain.
  • serv_addr.sin_addr.s_addr=INADDR_ANY;
    here sin_addr has one member s_addr which is used to hold the IP address of the machine its running in, and this IP ddress is got from the INADDR_ANY constant.
  • serv_addr.sin_port=htons(portno);
    here we hav to store port number into he sin_port member and this takes on the network bute order. So, htons() converts the host byte order representation of the port number to network byte order representation.
  • bind() – It is a system call that binds a socket to an address. Here, the address would be the IP address of the current machine and the port number.
    usage : bind(socket_fd, pointer_of_address_its_bound_to, size_of_address);
    Eg:  bind(sockfd, (struct sockaddr *)&serv_addr,sizeof(Serv_addr)_);
    On failure, it returns a value less than zero.
  • listen() –  This call allows a process to listen on socket for communication.
    usage: listen(socket_fd, no_of_waiting_connections);
    so it takes in a socket file descriptor and  the no. of connections waiting while the process is handling a particular connection. so they wait in a blocking queue.
    Eg : listen(sock_fd,5);
    so 5 connections can wait at the max.
  • accept() –  is a system call that causes the process to block until the client connects to the server.
    it returns a new descriptor and all communication should be carried out using the new file descriptor.
    usage: int accept(sockfd,pointer_to_address_of client, addr_storing_size_of_client_address);
    Eg:  newsockfd= accept(sockfd, (struct sockaddr *)&cli_addr,&clilen);
    here cli_len=sizeof(cli_addr); so the newsockfd has the new socket address which will be used for communication.
    So, this command blocks  until the read() of data is complete that is till the client has finished its write().
  • bzero(buffer,4096);
    n=read(newsockfd,buffer,4096);
    this initializes the buffer to zero and then reads the content from the socket (using the newsockfd) into the buffer. Her since the client sends the file name first, the buffer now contains the filename whose contents has to be sent back. The read(), will also block the process till there is something remaining to read from the socket.
  • fd=open(buffer,O_RDONLY) opens the file requested by the client in the read only mode.
  • read(fd,buffer,4096);
    this reads the contents of the file into the buffer. In socket programming, all communications happens using the buffer both at client and server side. With the completion of this read , the contents of the file is residing in the buffer and is ready to be sent to the client.
  • write(newsockfd,buffer,4096);
    this is the final command ehich writes the contents of the buffer( which has the file content) into the socket using the newsockfd which finally delivers it to the client process.

Functions used in client program:

  • connect() –  is a function used by the client to establish a connetion to the server. It takes 3 arguements:
    usage: connect(sockfd, host_to_which_itconnects, sizeof_addr);
    Eg: connect(sockfd,(struct sockaddr*)&serv_addr,sizeof(Serv_addr))<0);
  • The client too like the server uses the write() to send the file name to server and uses read() to read the content of the file into the buffer. It finally uses write(1,buffer,4096) to print the content on file onto standard output.

The full source code and usage for the client and server can be found here.

Advertisements

Hack 1 : Running a C program without main()

This  post is a simple example of deception. It shows how a programmer can defy the very important rule of having a main() in c program and still make the program run. This illustrates the concept on a simple program though it can be scaled to  much bigger and more complex programs.

#include<stdio.h>

#define decode(s,t,u,m,p,e,d) m##s##u##t
#define begin decode(a,n,i,m,a,t,e)

int begin()

{
printf(” hello “);
}

This program runs without main().

how??

Here we are using preprocessor(a program which processes the source code before compilation.) directive #define with arguments to give an impression that the program runs without main. But in reality it runs with a hidden main function.

The ‘##‘ operator is called the token pasting or token merging operator. That is we can merge two or more characters with it.

In the 2nd line of the program-

#define decode(s,t,u,m,p,e,d) m##s##u##t

What is the preprocessor doing here. The macro decode(s,t,u,m,p,e,d) is being expanded as “msut” (The ## operator merges m,s,u & t into msut). The logic is when you pass (s,t,u,m,p,e,d) as argument it merges the 4th,1st,3rd & the 2nd characters(tokens)

Now look at the third line of the program –

#define begin decode(a,n,i,m,a,t,e)

Here the preprocessor replaces the macro “begin” with the expansion decode(a,n,i,m,a,t,e). According to the macro definition in the previous line the argument must be expanded so that the 4th,1st,3rd & the 2nd characters must be merged. In the argument (a,n,i,m,a,t,e) 4th,1st,3rd & the 2nd characters are ‘m’,’a’,’i’ & ‘n’.

So the third line “int begin” is replaced by “int main” by the preprocessor before the program is passed on for the compiler. That’s it…

So actually C program can never run without a main() . We are just disguising the main() with the preprocessor, but actually there exists a hidden main function in the program.