You are on page 1of 14

SOCKET PROGRAMMING

Most interprocess communication uses the client server model. These terms refer to the
two processes which will be communicating with each other. One of the two processes,
the client, connects to the other process, the server, typically to make a request for
information. A good analogy is a person who makes a phone call to another person.
Notice that the client needs to know of the existence of and the address of the server, but
the server does not need to know the address of (or even the existence of) the client prior
to the connection being established. Notice also that once a connection is established,
both sides can send and receive information.
The system calls for establishing a connection are somewhat different for the client
and the server, but both involve the basic construct of a socket. A socket is one end of
an interprocess communication channel. The two processes each establish their own
socket.
The steps involved in establishing a socket on the client side are as follows:
1. Create a socket with the socket() system call
2. Connect the socket to the address of the server using the connect() system call
3. Send and receive data. There are a number of ways to do this, but the simplest is
to use the read() and write() system calls.
The steps involved in establishing a socket on the server side are as follows:
1. Create a socket with the socket() system call
2. Bind the socket to an address using the bind() system call. For a server socket on
the Internet, an address consists of a port number on the host machine.
3. Listen for connections with the listen() system call
4. Accept a connection with the accept() system call. This call typically blocks until
a client connects with the server.
5. Send and receive data

Socket Types
When a socket is created, the program has to specify the address domain and the socket
type. Two processes can communicate with each other only if their sockets are of the
same type and in the same domain. There are two widely used address domains, the unix
domain, in which two processes which share a common file system communicate, and
the Internet domain, in which two processes running on any two hosts on the Internet
communicate. Each of these has its own address format.
The address of a socket in the Unix domain is a character string which is basically an
entry in the file system.

1/14

252579906.doc

The address of a socket in the Internet domain consists of the Internet address of the host
machine (every computer on the Internet has a unique 32 bit address, often referred to as
its IP address). In addition, each socket needs a port number on that host. Port numbers
are 16 bit unsigned integers. The lower numbers are reserved in Unix for standard
services. For example, the port number for the FTP server is 21. It is important that
standard services be at the same port on all computers so that clients will know their
addresses. However, port numbers above 2000 are generally available.
There are two widely used socket types, stream sockets, and datagram sockets. Stream
sockets treat communications as a continuous stream of characters, while datagram
sockets have to read entire messages at once. Each uses its own communications
protocol. Stream sockets use TCP (Transmission Control Protocol), which is a reliable,
stream oriented protocol, and datagram sockets use UDP (Unix Datagram Protocol),
which is unreliable and message oriented.
socket: creates a socket of a given domain, type, protocol (buy a phone)
bind: assigns a name to the socket (get a telephone number)
listen: species the number of pending connections that can be queued for a server
socket. (call waiting allowance)
accept: server accepts a connection request from a client (answer phone)
connect: client requests a connection request to a server (call)
send, sendto: write to connection (speak)
recv, recvfrom: read from connection (listen)
shutdown: end the call

Server code
#include <stdio.h> : This header file contains declarations used in most input and output
and is typically included in all C programs.
#include <sys/types.h> This header file contains definitions of a number of data types
used in system calls. These types are used in the next two include files.
#include <sys/socket.h> The header file socket.h includes a number of definitions of
structures needed for sockets.
#include <netinet/in.h> The header file netinet/in.h contains constants and structures
needed for internet domain addresses.
2/14

252579906.doc

void error(char *msg)


{
perror(msg);
exit(1);
} This function is called when a system call fails. It displays a message about the error
on stderr and then aborts the program.
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen, n;
sockfd and newsockfd are file descriptors, i.e. array subscripts into the file descriptor
table . These two variables store the values returned by the socket system call and the
accept system call.
portno stores the port number on which the server accepts connections.
clilen stores the size of the address of the client. This is needed for the accept system call.
n is the return value for the read() and write() calls; i.e. it contains the number of
characters read or written.
char buffer[256]; :- The server reads characters from the socket connection into this
buffer.
struct sockaddr_in serv_addr, cli_addr; :- A sockaddr_in is a structure containing an
internet address. This structure is defined in <netinet/in.h>. Here is the definition:
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
An in_addr structure, defined in the same header file, contains only one field, a unsigned
long called s_addr. The variable serv_addr will contain the address of the server,
and cli_addr will contain the address of the client which connects to the server.
if (argc < 2) {
fprintf(stderr,"ERROR, no port provided\n");
exit(1);
}
The user needs to pass in the port number on which the server will accept connections as
an argument. This code displays an error message if the user fails to do this.

3/14

252579906.doc

sockfd = socket(AF_INET, SOCK_STREAM, 0);


if (sockfd < 0)
error("ERROR opening socket");
The socket() system call creates a new socket. It takes three arguments. The first is the
address domain of the socket. Recall that there are two possible address domains, the
unix domain for two processes which share a common file system, and the Internet
domain for any two hosts on the Internet. The symbol constantAF_UNIX is used for the
former, and AF_INET for the latter (there are actually many other options which can be
used here for specialized purposes).
The second argument is the type of socket. Recall that there are two choices here, a
stream socket in which characters are read in a continuous stream as if from a file or pipe,
and a datagram socket, in which messages are read in chunks. The two symbolic
constants are SOCK_STREAM and SOCK_DGRAM. The third argument is the
protocol. If this argument is zero (and it always should be except for unusual
circumstances), the operating system will choose the most appropriate protocol. It will
choose TCP for stream sockets and UDP for datagram sockets.
The socket system call returns an entry into the file descriptor table (i.e. a small integer).
This value is used for all subsequent references to this socket. If the socket call fails, it
returns -1. In this case the program displays and error message and exits. However, this
system call is unlikely to fail.
bzero((char *) &serv_addr, sizeof(serv_addr));
The function bzero() sets all values in a buffer to zero. It takes two arguments, the first is
a pointer to the buffer and the second is the size of the buffer. Thus, this line
initializes serv_addr to zeros.
portno = atoi(argv[1]);
The port number on which the server will listen for connections is passed in as an
argument, and this statement uses the atoi() function to convert this from a string of digits
to an integer.
serv_addr.sin_family = AF_INET;
The variable serv_addr is a structure of type struct sockaddr_in. This structure has four
fields. The first field is short sin_family, which contains a code for the address family. It
should always be set to the symbolic constant AF_INET.
serv_addr.sin_port = htons(portno);
The second field of serv_addr is unsigned short sin_port , which contain the port number.
However, instead of simply copying the port number to this field, it is necessary to
convert this to network byte order using the function htons() which converts a port
number in host byte order to a port number in network byte order.

4/14

252579906.doc

serv_addr.sin_addr.s_addr = INADDR_ANY;
The third field of sockaddr_in is a structure of type struct in_addr which contains only a
single field unsigned long s_addr. This field contains the IP address of the host. For
server code, this will always be the IP address of the machine on which the server is
running, and there is a symbolic constant INADDR_ANYwhich gets this address.
if (bind(sockfd, (struct sockaddr *) &serv_addr,
sizeof(serv_addr)) < 0)
error("ERROR on binding");
The bind() system call binds a socket to an address, in this case the address of the current
host and port number on which the server will run. It takes three arguments, the socket
file descriptor, the address to which is bound, and the size of the address to which it is
bound. The second argument is a pointer to a structure of type sockaddr, but what is
passed in is a structure of type sockaddr_in, and so this must be cast to the correct type.
This can fail for a number of reasons, the most obvious being that this socket is already in
use on this machine.
listen(sockfd,5);
The listen system call allows the process to listen on the socket for connections. The first
argument is the socket file descriptor, and the second is the size of the backlog queue, i.e.,
the number of connections that can be waiting while the process is handling a particular
connection. This should be set to 5, the maximum size permitted by most systems. If the
first argument is a valid socket, this call cannot fail, and so the code doesn't check for
errors.
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
The accept() system call causes the process to block until a client connects to the server.
Thus, it wakes up the process when a connection from a client has been successfully
established. It returns a new file descriptor, and all communication on this connection
should be done using the new file descriptor. The second argument is a reference pointer
to the address of the client on the other end of the connection, and the third argument is
the size of this structure.
bzero(buffer,256);
n = read(newsockfd,buffer,255);
if (n < 0) error("ERROR reading from socket");
printf("Here is the message: %s\n",buffer);
Note that we would only get to this point after a client has successfully connected to our
server. This code initializes the buffer using the bzero() function, and then reads from the
socket. Note that the read call uses the new file descriptor, the one returned by accept(),

5/14

252579906.doc

not the original file descriptor returned by socket(). Note also that the read() will block
until there is something for it to read in the socket, i.e. after the client has executed
a write(). It will read either the total number of characters in the socket or 255, whichever
is less, and return the number of characters read.
n = write(newsockfd,"I got your message",18);
if (n < 0) error("ERROR writing to socket");
Once a connection has been established, both ends can both read and write to the
connection. Naturally, everything written by the client will be read by the server, and
everything written by the server will be read by the client. This code simply writes a short
message to the client. The last argument of write is the size of the message.
return 0;
}
This terminates main and thus the program. Since main was declared to be of type int as
specified by the ascii standard, many compilers complain if it does not return anything.

6/14

252579906.doc

Client code
As before, we will go through the program client.c line by line.
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
The header files are the same as for the server with one addition. The file netdb.h defines
the structure hostent, which will be used below.
void error(char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
The error() function is identical to that in the server, as are the variables sockfd, portno,
and n. The variable serv_addr will contain the address of the server to which we want to
connect. It is of type struct sockaddr_in.
The variable server is a pointer to a structure of type hostent. This structure is defined in
the header file netdb.h as follows:
struct hostent {
char *h_name;
/* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length;
/* length of address */
char **h_addr_list; /* list of addresses from name server */
#define h_addr h_addr_list[0] /* address, for backward compatiblity */
};
It defines a host computer on the Internet. The members of this structure are:
h_name
Official name of the host.
h_aliases A zero terminated array of alternate
names for the host.
h_addrtype The type of address being returned;
currently always AF_INET.
7/14

252579906.doc

h_length

The length, in bytes, of the address.

h_addr_list A pointer to a list of network addresses


for the named host. Host addresses are
returned in network byte order.
Note that h_addr is an alias for the first address in the array of network addresses.
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
All of this code is the same as that in the server.
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
argv[1] contains the name of a host on the Internet, e.g. cheerios@cs.rpi.edu. The
function:
struct hostent *gethostbyname(char *name)
Takes such a name as an argument and returns a pointer to a hostent containing
information about that host. The field char *h_addr contains the IP address. If this
structure is NULL, the system could not locate a host with this name.
The mechanism by which this function works is complex, often involves querying large
databases all around the country.
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
This code sets the fields in serv_addr. Much of it is the same as in the server. However,
because the field server->h_addr is a character string, we use the function:
void bcopy(char *s1, char *s2, int length)
which copies length bytes from s1 to s2.

8/14

252579906.doc

if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
The connect function is called by the client to establish a connection to the server. It takes
three arguments, the socket file descriptor, the address of the host to which it wants to
connect (including the port number), and the size of this address. This function returns 0
on success and -1 if it fails.
Notice that the client needs to know the port number of the server, but it does not need to
know its own port number. This is typically assigned by the system whenconnect is
called.
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
if (n < 0)
error("ERROR reading from socket");
printf("%s\n",buffer);
return 0;
}
The remaining code should be fairly clear. It prompts the user to enter a message,
uses fgets to read the message from stdin, writes the message to the socket, reads the
reply from the socket, and displays this reply on the screen.
Enhancements to the server code
The sample server code above has the limitation that it only handles one connection, and
then dies. A "real world" server should run indefinitely and should have the capability of
handling a number of simultaneous connections, each in its own process. This is typically
done by forking off a new process to handle each new connection.
The following code has a dummy function called dostuff(int sockfd). This function will
handle the connection after it has been established and provide whatever services the
client requests. As we saw above, once a connection is established, both ends can
use read and write to send information to the other end, and the details of the information
passed back and forth do not concern us here. To write a "real world" server, you would
make essentially no changes to the main() function, and all of the code which provided
the service would be in dostuff().
To allow the server to handle multiple simultaneous connections, we make the following
changes to the code:

9/14

252579906.doc

1. Put the accept statement and the following code in an infinite loop.
2. After a connection is established, call fork() to create a new process.
3. The child process will close sockfd and call dostuff, passing the new socket file
descriptor as an argument. When the two processes have completed their
conversation, as indicated by dostuff() returning, this process simply exits.
4. The parent process closes newsockfd. Because all of this code is in an infinite
loop, it will return to the accept statement to wait for the next connection.
Here is the code.
while (1) {
newsockfd = accept(sockfd,
(struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0)
error("ERROR on accept");
pid = fork();
if (pid < 0)
error("ERROR on fork");
if (pid == 0) {
close(sockfd);
dostuff(newsockfd);
exit(0);
}
else close(newsockfd);
} /* end of while */
Alternative types of sockets
This example showed a stream socket in the Internet domain. This is the most common
type of connection. A second type of connection is a datagram socket. You might want to
use a datagram socket in cases where there is only one message being sent from the client
to the server, and only one message being sent back. There are several differences
between a datagram socket and a stream socket.
1. Datagrams are unreliable, which means that if a packet of information gets lost
somewhere in the Internet, the sender is not told (and of course the receiver does
not know about the existence of the message). In contrast, with a stream socket,
the underlying TCP protocol will detect that a message was lost because it was not
acknowledged, and it will be retransmitted without the process at either end
knowing about this.
2. Message boundaries are preserved in datagram sockets. If the sender sends a
datagram of 100 bytes, the receiver must read all 100 bytes at once. This can be
contrasted with a stream socket, where if the sender wrote a 100 byte message, the
receiver could read it in two chunks of 50 bytes or 100 chunks of one byte.
3. The
communication
is
done
using
special
system
calls sendto() and receivefrom() rather than the more generic read() and write().
4. There is a lot less overhead associated with a datagram socket because
connections do not need to be established and broken down, and packets do not

10/14

252579906.doc

need to be acknowledged. This is why datagram sockets are often used when the
service to be provided is short, such as a time-of-day service.

/* tcpserver.c */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
int main()
{
int sock, connected, bytes_recieved , true = 1;
char send_data [1024] , recv_data[1024];
struct sockaddr_in server_addr,client_addr;
int sin_size;
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Socket");
exit(1);
}
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&true,sizeof(int)) == -1) {
perror("Setsockopt");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5000);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero),8);
if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))
== -1) {
perror("Unable to bind");
exit(1);
}
if (listen(sock, 5) == -1) {
11/14

252579906.doc

perror("Listen");
exit(1);
}
printf("\nTCPServer Waiting for client on port 5000");
fflush(stdout);
while(1)
{
sin_size = sizeof(struct sockaddr_in);
connected = accept(sock, (struct sockaddr *)&client_addr,&sin_size);
printf("\n I got a connection from (%s , %d)",
inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
while (1)
{
printf("\n SEND (q or Q to quit) : ");
gets(send_data);
if (strcmp(send_data , "q") == 0 || strcmp(send_data , "Q") == 0)
{
send(connected, send_data,strlen(send_data), 0);
close(connected);
break;
}
else
send(connected, send_data,strlen(send_data), 0);
bytes_recieved = recv(connected,recv_data,1024,0);
recv_data[bytes_recieved] = '\0';
if (strcmp(recv_data , "q") == 0 || strcmp(recv_data , "Q") == 0)
{
close(connected);
break;
}
else
printf("\n RECIEVED DATA = %s " , recv_data);
fflush(stdout);

12/14

252579906.doc

}
}
close(sock);
return 0;
}

/* tcpclient.c */
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main()
{
int sock, bytes_recieved;
char send_data[1024],recv_data[1024];
struct hostent *host;
struct sockaddr_in server_addr;
host = gethostbyname("127.0.0.1");
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(5000);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(server_addr.sin_zero),8);
if (connect(sock, (struct sockaddr *)&server_addr,
sizeof(struct sockaddr)) == -1)
{
perror("Connect");
exit(1);
13/14

252579906.doc

}
while(1)
{
bytes_recieved=recv(sock,recv_data,1024,0);
recv_data[bytes_recieved] = '\0';
if (strcmp(recv_data , "q") == 0 || strcmp(recv_data , "Q") == 0)
{
close(sock);
break;
}
else
printf("\nRecieved data = %s " , recv_data);
printf("\nSEND (q or Q to quit) : ");
gets(send_data);
if (strcmp(send_data , "q") != 0 && strcmp(send_data , "Q") != 0)
send(sock,send_data,strlen(send_data), 0);
else
{
send(sock,send_data,strlen(send_data), 0);
close(sock);
break;
}
}
return 0;
}

14/14

252579906.doc

You might also like