Home![]() ![]() Network ![]() ![]() Graphics ![]() ![]() ![]() Programming ![]() ![]() ![]() Misc ![]() System ![]() ![]() ![]() ![]() Help out Distributions specific ![]() ![]() contact interesting sites ![]() manpages tools FAQ Sitemap Imprint poll results Last additions: using iotop to find disk usage hogs using iotop to find disk usage hogs words:887 views:203621 userrating:average rating: 1.6 (126 votes) (1=very good 6=terrible) May 25th. 2007: Words why adblockers are bad486 Views254734 Workaround and fixes for the current Core Dump Handling vulnerability affected kernels Workaround and fixes for the current Core Dump Handling vulnerability affected kernels words:161 views:144938 userrating:average rating: 1.3 (66 votes) (1=very good 6=terrible) April, 26th. 2006: Words New subdomain: toolsntoys.linuxhowtos.org38 Views103615 How to force a check of the file systems How to force a check of the file systems words:179 views:35993 userrating:average rating: 1.3 (87 votes) (1=very good 6=terrible) Oct, 18th. 2005: Words New features online: interactive search function34 Views57168 Sep, 5th. 2005: Words New website design online51 Views45034 Aug, 27th 2005: Words Existing articles can be edited by anyone now.134 Views12382 | Sockets TutorialThis is a simple tutorial on using sockets for interprocess communication.The client server modelMost 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. The steps involved in establishing a socket on the client side are as follows:
The steps involved in establishing a socket on the server side are as follows:
Socket TypesWhen 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). 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 communciations 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. The examples in this tutorial will use sockets in the Internet domain using the TCP protocol. Sample codeC code for a very simple client and server are provided for you. These communicate using stream sockets in the Internet domain. The code is described in detail below. However, before you read the descriptions and look at the code, you should compile and run the two programs to see what they do. Download these into files called They probably won't require any special compiling flags, but on some solaris systems you may need to link to the socket library by appending Ideally, you should run the client and the server on separate hosts on the Internet. Start the server first. Suppose the server is running on a machine called It's not supposed to do anything until a connection is made.
server 51717 To run the client you need to pass in two arguments, the name of the host on which the server is running and the port number on which the server is listening for connections. Here is the command line to connect to the server described above: client cheerios 51717 The client will prompt you to enter a message. If everything works correctly, the server will display your message on stdout, send an acknowledgement message to the client and terminate. The client will print the acknowledgement message from the server and then terminate. You can simulate this on a single machine by running the server in one window and the client in another. In this case, you can use the keyword The server code uses a number of ugly programming constructs, and so we will go through it line by line.
void error(char *msg) { This function is called when a system call fails. It displays a message about the error on stderr and then aborts the program. The perror man page gives more information.int main(int argc, char *argv[]) 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.
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 .
struct sockaddr_in An in_addr structure, defined in the same header file, contains only one field, a unsigned long called s_addr .The variable if (argc < 2) 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. sockfd = socket(AF_INET, SOCK_STREAM, 0); 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 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 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.
This is a simplified description of the socket call; there are numerous other choices for domains and types, but these are the most common. The socket() man page has more information. 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) 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. The bind() manual has more information.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. The listen() man page has more information.clilen = sizeof(cli_addr); 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. The accept() man page has more information.bzero(buffer,256); 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() , 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. The read() man page has more information. n = write(newsockfd,"I got your message",18); 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. The write() man page has more information. return 0; This terminates main and thus the program. Since main was declared to be of type int as specified by the ascii standard, some compilers complain if it does not return anything. Client codeAs before, we will go through the program client.c line by line. #include <stdio.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) 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 struct hostent It defines a host computer on the Internet. The members of this structure are: h_name Official name of the host. Note that h_addr is an alias for the first address in the array of network addresses.char buffer[256]; All of this code is the same as that in the server. server = gethostbyname(argv[1]); The variable argv [1] contains the name of a host on the Internet, e.g. 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
In the old days, this function worked by searching a system file called bzero((char *) &serv_addr, sizeof(serv_addr)); 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 . ----if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0) 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. The connect() man page has more information.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 when printf("Please enter the message: "); 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 codeThe 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
To allow the server to handle multiple simultaneous connections, we make the following changes to the code:
Here is the code. while (1) Click here for a complete server program which includes this change. This will run with the program client.c. The zombie problemThe above code has a problem; if the parent runs for a long time and accepts many connections, each of these connections will create a zombie when the connection is terminated. A zombie is a process which has terminated but but cannot be permitted to fully die because at some point in the future, the parent of the process might execute a signal(SIGCHLD,SIG_IGN); This says to ignore the SIGCHLD signal. However, on systems running SunOS, you have to use the following code: void *SigCatcher(int n) The function SigCatcher() will be called whenever the parent receives a SIGCHLD signal (i.e. whenever a child dies). This will in turn call wait3 which will receive the signal. The WNOHANG flag is set, which causes this to be a non-blocking wait (one of my favorite oxymorons).Alternative types of socketsThis 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.
Click here for the server code using a datagram socket. Click here for the client code using a datagram socket. These two programs can be compiled and run in exactly the same way as the server and client using a stream socket. Most of the server code is similar to the stream socket code. Here are the differences. sock=socket(AF_INET, SOCK_DGRAM, 0); Note that when the socket is created, the second argument is the symbolic constant SOCK_DGRAM instead of SOCK_STREAM. The protocol will be UDP, not TCP. ---- fromlen = sizeof(struct sockaddr_in); Servers using datagram sockets do not use the listen() or the accept() system calls. After a socket has been bound to an address, the program calls recvfrom() to read a message. This call will block until a message is received. The recvfrom() system call takes six arguments. The first three are the same as those for the read() call, the socket file descriptor, the buffer into which the message will be read, and the maximum number of bytes. The fourth argument is an integer argument for flags. This is ordinarily set to zero. The fifth argument is a pointer to a sockaddr_in structure. When the call returns, the values of this structure will have been filled in for the other end of the connection (the client). The size of this structure will be in the last argument, a pointer to an integer. This call returns the number of bytes in the message. (or -1 on an error condition). The recfrom() man page has more information.n = sendto(sock,"Got your message To send a datagram, the function sendto() is used. This also takes six arguments. The first three are the same as for a write() call, the socket file descriptor, the buffer from which the message will be written, and the number of bytes to write. The fourth argument is an int argument called flags, which is normally zero. The fifth argument is a pointer to a sockadd_in structure. This will contain the address to which the message will be sent. Notice that in this case, since the server is replying to a message, the values of this structure were provided by the recvfrom call. The last argument is the size of this structure. Note that this is not a pointer to an int, but an int value itself. The sendto() man page has more information.The client code for a datagram socket client is the same as that for a stream socket with the following differences.
Sockets in the Unix DomainHere is the code for a client and server which communicate using a stream socket in the Unix domain. The only difference between a socket in the Unix domain and a socket in the Internet domain is the form of the address. Here is the address structure for a Unix Domain address, defined in the header file. struct sockaddr_un The field sun_path has the form of a path name in the Unix file system. This means that both client and server have to be running the same file system. Once a socket has been created, it remain until it is explicitly deleted, and its name will appear with the ls command, always with a size of zero. Sockets in the Unix domain are virtually identical to named pipes (FIFOs).Designing serversThere are a number of different ways to design servers. These models are discussed in detail in a book by Douglas E. Comer and David L. Stevens entiteld Internetworking with TCP/IP Volume III:Client Server Programming and Applications published by Prentice Hall in 1996. These are summarized here. Concurrent, connection oriented servers
Iterative, connectionless servers
Single Process concurrent servers
rate this article: current rating: average rating: 1.2 (41354 votes) (1=very good 6=terrible) Your rating: back |