Introduction to Socket Program in Networking By CodeTextPro || Hanif Miah

Socket Programming in Networking



socket program


The client-server model
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.


TCreate a socket with the socket() system call

1. Connect the socket to the address of the server using the connect() system call
2. 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.



Send and receive data he steps involved in establishing a socket on the client side are as follows:

3. Create a socket with the socket() system call
4. Connect the socket to the address of the server using the connect() system call
5. 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.

• 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.



Coding technique

The server code uses a number of ugly programming constructs, and so we will go through it line by line.
#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. 
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.

Each running process has a file descriptor table which
contains pointers to all open i/o streams. When a process
starts, three entries are created in the first three cells of
the table. Entry 0 points to standard input, entry 1 points
to standard output, and entry 2 points to standard error.
Whenever a file is opened, a new entry is created in this

table, usually in the first available empty slot.


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, an 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. 
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 constant AF_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 an 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. 
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_ANY which 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 accepting (), 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. 
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.
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.


perror() produces a short error message on the standard

error describing the last error encountered during a call to a system or library
function. If _s is not a NULL pointer and does not point to a null string,
the string it points to is printed, followed by a colon, followed by a space,
followed by the message and a NEWLINE. If _s is a NULL pointer or points to a
null string, just the message is printed, followed by a NEWLINE. To be
of most use, the argument string should include the name of the program that
incurred the error. The error number is taken from the external variable
errno (see intro(2)), which is set when errors occur but not cleared when

non-erroneous calls are made.
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.



Post a Comment

2 Comments

  1. Today, I was just browsing along and came upon your blog. Just wanted to say good blog and this article helped me a lot, due to which I have found exactly I was looking. Windows Hosting

    ReplyDelete