Unix Sockets and Interaction
Passing Information Between Programs

by D.W. Hyatt

Note: Mr. Hyatt wishes to gratefully acknowledge the work on UNIX sockets by former TJHSST Systems Lab students, Glen Evans, Leo Wolpert, and Dan Willenson. Portions of their tech lab projects were used to develop this web page.

What is a Unix Socket?

A Unix socket is a connection between two computers over the Internet. Through sockets, it is possible to pass information between two different programs in order to create an interactive networked application. This could include projects where multiple users interact in a single application, or even a parallel programming project where the programmer directly controls connections between hosts.

Sockets will act much like reading and writing to files. In one program the server will write information to a socket, and in the other program the client will be able to read that information. It is possible for information exchange to be in both directions once the socket connection has been established.

This page is only an assist to get students involved in Supercomputer Applications started with sockets. For a more complete and well organized tutorial, check out the pages by Brian "Beej" Hall from his website on networks and sockets:

Creating a Socket

To create a socket, it is first necessary to call the function socket() which is defined in the appropriate header file. The integer it returns is called a socket file descriptor and in the example code below the variable sockfd is used. This socket file descriptor will be needed by the calls that allow for reading and writing between programs in much the same way normal file input and output works.
     #include  <sys/types.h>
     #include  <sys/socket.h>
     int socket(int family, int type, int protocol)
Where: Here is an example call to create the socket file descriptor.
     sockfd = socket(AF_NET, SOCK_STREAM, 0);

Initializing the Socket

It is essential to have some other variables around that hold information related to the computer connections and the socket. These structures are defined in various header files and the variable fields need to be initialized before making the connection.

     #include  <netinet/in.h>    //for sockaddr_in structure
     #include  <netdb.h>         //for hostent structure
     #define MYPORT 6994            // This is an arbitrary number

     struct sockaddr_in myaddr;     //data structure to hold socket info 
     char ip_addr[MAXIP];           //ip address such as 198.38.17.1
     char hostname[MAXNAME];        //host name such as station1.tjhsst.edu 

Since the data structures could have garbage values that could cause problems, the memset() function can be used to initialize all values to zero. An alternative function, bzero(), accomplishes the same thing but this function is being deprecated.


     memset(&myaddr, 0, sizeof(myaddr));  //clears out data structure  
     myaddr.sin_family = AF_INET;         //fill in address family
     myaddr.sin_port = htons(MYPORT);     //assign port in network byte order  
  
     gethostname(hostname,sizeof(hostname)); //this gets the hostname of the local computer

     struct hostent *hp;         // pointer to host entity
     hp=gethostbyname(hostname); // assign the value

     memcpy(addr.sin_addr, hp->h_addr, hp->h_length); //copy some data from sockaddr_in to hostent
  
     strcpy(ip_addr,inet_ntoa(*((struct in_addr *)hp->h_addr))); //get the IP Address from the hostent structure

Communicating Between Sockets

Once the information is put in the socket data structures, it is now possible to communicate between two systems. There are several important functions such as bind(), listen(), and accept() on the server side, and connect() on the client side that are required to set up the specific socket for communication.

On the server side, the following is typical:

     //sockfd  has been initialized as previously stated
     //myaddr has been initialized as previously stated

     bind(sockfd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr));
      //this binds a waiting socket to the socket file descriptor
     listen(sockfd,3);
      //this listens on the socket for up to three(3) incoming messages from other sockets
     int newfd=accept(sockfd,NULL,NULL);
      //this pauses the program and accepts the first nonlocal socket to make a connection
      //at which point the program continues
      //newfd = the file descriptor for the new socket connection which becomes established
On the client side, the following connects to the server.
     //sockfd and myaddr have already been initialized
     connect(sockfd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr));
For the actual data transfer, a character array buffer needs to be created to hold the information that is being received. The functions that send the data are typically send() and recv(), although read() and write() can also be used.

On the server side the following is typical:

     send(newfd, message, strlen(message), 0)
On the client side this is how the program receives the message:
     #define MAXDATA 100
     char buffer[MAXDATA];

     numbytes=recv(sockfd, buffer, MAXDATA, 0)
Similarly, read and write could be used.
     read(sockfd, buffer, MAXDATA);

     write(sockfd, buffer, sizeof(buffer))

Putting It All Together

The following programs are adaptations inspired by BeeJ's website that have been modified by Leo Wolpert ('01) and Mr. Hyatt. Both programs use the function perror() that prints out an error message to the screen should something go wrong which is very helpful for programs of this nature. For instance, if the initial socket call does not work, the following code will cause the error message to be printed and the program will terminate with the call to exit().
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("Error getting socket descriptor");
        exit(1);
    }

The server program in the following example must be started first, and then it waits for the client program on another machine to connect to it. Once the connection is made, the person sitting at the server program types in a string of letters, and that string is then printed out on the client's terminal.

The server and client programs below demonstrate simple one way communication. However, SOCK_STREAM supports two way communication through the same port, so by adding a send() function to the client side and recv() to the server side, messages can be sent both ways through the same socket creating a simple "chat" program.