Linux Network Programming, Part 1
February 1st, 1998 by Ivan Griffin and John Nelson in
Like most other Unix-based operating systems, Linux supports TCP/IP as its native network transport. In this series, we will assume you are fairly familiar with C programming on Linux and with Linux topics such as signals, forking, etc.
This article is a basic introduction to using the BSD socket interface for creating networked applications. In the next article, we will deal with issues involved in creating (network) daemon processes. Future articles will cover using remote procedure calls and developing with CORBA/distributed objects.
The TCP/IP suite of protocols allows two applications, running on either the same or separate computers connected by a network, to communicate. It was specifically designed to tolerate an unreliable network. TCP/IP allows two basic modes of operation—connection-oriented, reliable transmission and connectionless, unreliable transmission (TCP and UDP respectively). Figure 1 illustrates the distinct protocol layers in the TCP/IP suite stack.
Figure 1. TCP/IP Protocol Layers
TCP provides sequenced, reliable, bi-directional, connection-based bytestreams with transparent retransmission. In English, TCP breaks your messages up into chunks (not greater in size than 64KB) and ensures that all the chunks get to the destination without error and in the correct order. Being connection-based, a virtual connection has to be set up between one network entity and the other before they can communicate. UDP provides (very fast) connectionless, unreliable transfer of messages (of a fixed maximum length).
To allow applications to communicate with each other, either on the same machine (using loopback) or across different hosts, each application must be individually addressable.
TCP/IP addresses consist of two parts—an IP address to identify the machine and a port number to identify particular applications running on that machine.
The addresses are normally given in either the “dotted-quad” notation (i.e., 127.0.0.1) or as a host name (foobar.bundy.org). The system can use either the /etc/hosts file or the Domain Name Service (DNS) (if available) to translate host names to host addresses.
Port numbers range from 1 upwards. Ports between 1 and IPPORT_RESERVED (defined in /usr/include/netinet/in.h—typically 1024) are reserved for system use (i.e., you must be root to create a server to bind to these ports).
The simplest network applications follow the client-server model. A server process waits for a client process to connect to it. When the connection is established, the server performs some task on behalf of the client and then usually the connection is broken.
The most popular method of TCP/IP programming is to use the BSD socket interface. With this, network endpoints (IP address and port number) are represented as sockets.
The socket interprocess communication (IPC) facilities (introduced with 4.2BSD) were designed to allow network-based applications to be constructed independently of the underlying communication facilities.
To create a server application using the BSD interface, you must follow these steps:
Create a new socket by typing: socket().
bind an address (IP address and port number) to the socket by typing: bind. This step identifies the server so that the client knows where to go.
listen for new connection requests on the socket by typing: listen().
accept new connections by typing: accept().
Often, the servicing of a request on behalf of a client may take a considerable length of time. It would be more efficient in such a case to accept and deal with new connections while a request is being processed. The most common way of doing this is for the server to fork a new copy of itself after accepting the new connection.
Figure 2. Representation of Client/Server Code
The code example in Listing 1 shows how servers are implemented in C. The program expects to be called with only one command-line argument: the port number to bind to. It then creates a new socket to listen on using the socket() system call. This call takes three parameters: the domain in which to listen to, the socket type and the network protocol.
The domain can be either the PF_UNIX domain (i.e., internal to the local machine only) or the PF_INET (i.e., all requests from the Internet). The socket type specifies the communication semantics of the connection. While a few types of sockets have been specified, in practice, SOCK_STREAM and SOCK_DGRAM are the most popular implementations. SOCK_STREAM provides for TCP reliable connection-oriented communications, SOCK_DGRAM for UDP connectionless communication.
The protocol parameter identfies the particular protocol to be used with the socket. While multiple protocols may exist within a given protocol family (or domain), there is generally only one. For TCP this is IPPROTO_TCP, for UDP it is IPPROTO_UDP. You do not have to explicitly specify this parameter when making the function call. Instead, using a value of 0 will select the default protocol.
Once the socket is created, its operation can be tweaked by means of socket options. In the above example, the socket is set to reuse old addresses (i.e., IP address + port numbers) without waiting for the required connection close timeout. If this were not set, you would have to wait four minutes in the TIME_WAIT state before using the address again. The four minutes comes from 2 * MSL. The recommended value for MSL, from RFC 1337, is 120 seconds. Linux uses 60 seconds, BSD implementations normally use around 30 seconds.
The socket can linger to ensure that all data is read, once one end closes. This option is turned on in the code. The structure of linger is defined in /usr/include/linux/socket.h. It looks like this:
struct linger
{
int l_onoff; /* Linger active */
int l_linger; /* How long to linger */
};
If l_onoff is zero, lingering is disabled. If it is non-zero, lingering is enabled for the socket. The l_linger field specifies the linger time in seconds.
The server then tries to discover its own host name. I could have used the gethostname() call, but the use of this function is deprecated in SVR4 Unix (i.e., Sun's Solaris, SCO Unixware and buddies), so the local function _GetHostName() provides a more portable solution.
Once the host name is established, the server constructs an address for the socket by trying to resolve the host name to an Internet domain address, using the gethostbyname() call. The server's IP address could instead be set to INADDR_ANY to allow a client to contact the server on any of its IP addresses—used, for example, with a machine with multiple network cards or multiple addresses per network card.
After an address is created, it is bound to the socket. The socket can now be used to listen for new connections. The BACK_LOG specifies the maximum size of the listen queue for pending connections. If a connection request arrives when the listen queue is full, it will fail with a connection refused error. [This forms the basis for one type of denial of service attack —Ed.] See sidebar on TCP listen() Backlog.
Having indicated a willingness to listen to new connection requests, the socket then prepares to accept the requests and service them. The example code achieves this using an infinite for() loop. Once a connection has been accepted, the server can ascertain the address of the client for logging or other purposes. It then forks a child copy of itself to handle the request while it (the parent) continues listening for and accepting new requests.
The child process can use the read() and write() system calls on this connection to communicate with the client. It is also possible to use the buffered I/O on these connections (e.g., fprint()) as long as you remember to fflush() the output when necessary. Alternatively, you can disable buffering altogether for the process (see the setvbuf() (3) man page).
As you can see from the code, it is quite common (and good practice) for the child processes to close the inherited parent-socket file descriptor, and for the parent to close the child-socket descriptor when using this simple forking model.
The client code, shown in Listing 2, is a little simpler than the corresponding server code. To start the client, you must provide two command-line arguments: the host name or address of the machine the server is running on and the port number the server is bound to. Obviously, the server must be running before any client can connect to it.
In the client example (Listing 2), a socket is created like before. The first command-line argument is first assumed to be a host name for the purposes of finding the server's address. If this fails, it is then assumed to be a dotted-quad IP address. If this also fails, the client cannot resolve the server's address and will not be able to contact it.
Having located the server, an address structure is created for the client socket. No explicit call to bind() is needed here, as the connect() call handles all of this.
Once the connect() returns successfully, a duplex connection has been established. Like the server, the client can now use read() and write() calls to receive data on the connection.
Be aware of the following points when sending data over a socket connection:
Sending text is usually fine. Remember that different systems can have different conventions for the end of line (i.e., Unix is \012, whereas Microsoft uses \015\012).
Different architectures may use different byte-ordering for integers etc. Thankfully, the BSD guys thought of this problem already. There are routines (htons and nstoh for short integers, htonl and ntohl for long integers) which perform host-to-network order and network-to-host order conversions. Whether the network order is little-endian or big-endian doesn't really matter. It has been standardized across all TCP/IP network stack implementations. Unless you persistently pass only characters across sockets, you will run into byte-order problems if you do not use these routines. Depending on the machine architecture, these routines may be null macros or may actually be functional. Interestingly, a common source of bugs in socket programming is to forget to use these byte-ordering routines for filling the address field in the sock_addr structures. Perhaps it is not intuitively obvious, but this must also be done when using INADDR_ANY (i.e., htonl(INADDR_ANY)).
A key goal of network programming is to ensure processes do not interfere with each other in unexpected ways. In particular, servers must use appropriate mechanisms to serialize entry through critical sections of code, avoid deadlock and protect data validity.
You cannot (generally) pass a pointer to memory from one machine to another and expect to use it. It is unlikely you will want to do this.
Similarly, you cannot (generally) pass a file descriptor from one process to another (non-child) process via a socket and use it straightaway. Both BSD and SVR4 provide different ways of passing file descriptors between unrelated processes; however, the easiest way to do this in Linux is to use the /proc file system.
Additionally, you must ensure that you handle short writes correctly. Short writes happen when the write() call only partially writes a buffer to a file descriptor. They occur due to buffering in the operating system and to flow control in the underlying transport protocol. Certain system calls, termed slow system calls, may be interrupted. Some may or may not be automatically restarted, so you should explicitly handle this when network programming. The code excerpt in Listing 3 handles short writes.
Using multiple threads instead of multiple processes may lighten the load on the server host, thereby increasing efficiency. Context-switching between threads (in the same process address space) generally has much less associated overhead than switching between different processes. However, since most of the slave threads in this case are doing network I/O, they must be kernel-level threads. If they were user-level threads, the first thread to block on I/O would cause the whole process to block. This would result in starving all other threads of any CPU attention until the I/O had completed.
It is common to close unnecessary socket file descriptors in child and parent processes when using the simple forking model. This prevents the child or parent from potential erroneous reads or writes and also frees up descriptors, which are a limited resource. But do not try this when using threads. Multiple threads within a process share the same memory space and set of file descriptors. If you close the server socket in a slave thread, it closes for all other threads in that process.
Listing 4 shows a connectionless server using UDP. While UDP applications are similar to their TCP cousins, they have some important differences. Foremost, UDP does not guarantee reliable delivery—if you require reliability and are using UDP, you either have to implement it yourself in your application logic or switch to TCP.
Like TCP applications, with UDP you create a socket and bind an address to it. (Some UDP servers do not need to call bind(), but it does no harm and will save you from making a mistake.) UDP servers do not listen or accept incoming connections, and clients do not explicitly connect to servers. In fact, there is very little difference between UDP clients and servers. The server must be bound to a known port and address only so that the client knows where to send messages. Additionally, the order of expected data transmissions is reversed, i.e., when you send data using send() in the server, your client should expect to receive data using recv().
It is common for UDP clients to fill in the sockaddr_in structure with a sin_port value of 0. (Note that 0 in either byte-order is 0.) The system then automatically assigns an unused port number (between 1024 and 5000) to the client. I'm leaving it as an exercise to the reader to convert the server in Listing 4 into a UDP client.
In order to connect to a server, you must first know both the address and port number on which it is listening. Many common services (FTP, TELNET, etc.) are listed in a text database file called /etc/services. An interface exists to request a service by name and to receive the port number (correctly formatted in network byte-order) for that service. The function is getservbyname(), and its prototype is in the header file /usr/include/netdb.h. This example takes a service name and protocol type and returns a pointer to struct servent.
struct servent
{
char *s_name; /* official service name */
char **s_aliases; /* alias list */
int s_port; /* port number, network<\n>
* byte-order--so do not
* use host-to-network macros */
char *s_proto; /* protocol to use */
};
This article has introduced network programming in Linux, using C and the BSD Socket API. In general, coding with this API tends to be quite laborious, especially when compared to some of the other techniques available. In future articles, I will compare two alternatives to the BSD Socket API for Linux: the use of Remote Procedure Calls (RPCs) and the Common Object Request Broker Architecture (CORBA). RPCs were introduced in Ed Petron's article “Remote Procedure Calls” in Linux Journal Issue #42 (October, 1997).
Major System Calls The next article in this series will cover the issues involved in developing long-lived network services (daemons) in Linux.
All listings referred to in this article are available by anonymous download in the file ftp://ftp.ssc.com/lj/listings/issue46/2333.tgz.
Subscribe now!
Recently Popular
| What happens after TV's mainframe era ends next February? | Jul-05-08 |
| Why Python? | May-01-00 |
| Building a Call Center with LTSP and Soft Phones | Aug-25-05 |
| An Open Video to HP | Jul-02-08 |
| Chapter 16: Ubuntu and Your iPod | Aug-30-06 |
| Time to school the FCC on what "free" really means | Jul-02-08 |
Featured Video
From the Magazine
July 2008, #171
Heard of the Web? If not, read on. This month we talk with Matt Mullenweg about WordPress. If you want to get your hands dirty in Web code, take a look at the rest of our feature articles on WebKit, Dojo and OpenLaszlo.
In the rest of the issue, you'll find articles on OpenID, RDFa and Quanta Plus.
Kyle Rankin puts a new spin (as in "no" spin SSD) on hard drives and
also tells you how to migrate to that new disk (spinning or not).
Mick Bauer continues his series on customizing live CD's.
And, James Gray gives us a feel for the state of Linux in the enterprise.
After all that, you may need some TV time. If so, check out our review
on how to make that digital TV tuner card work in your Linux box.
Delicious
Digg
Reddit
Newsvine
Technorati






linux
On October 2nd, 2007 enigma downloader (not verified) says:
machines GNU/Linux clustered? Can be . Best regards.
apginrrn
On September 30th, 2007 Anonymous (not verified) says:
jfbuvdrf
gzjcpykq
On September 29th, 2007 Anonymous (not verified) says:
phmwnzgt
ylvtzipi
On September 8th, 2007 Anonymous (not verified) says:
gkpaxlsh
socket problem
On June 4th, 2007 Anonymous (not verified) says:
what are the reasons for error .....
WSAGetLastError()=10061
while connecting from linux to windows sockets..
Client-Manager-Server Applications
On May 29th, 2007 allan (not verified) says:
To Whom It May Concern:
Good day!
Instead of comments, I will ask for you advice on Socket Programming -> passing socket file descriptor from client to manager which in turn passes the client socket to the server but the server should communicate directly to the server using its socket file descriptor (i.e. write(client_sockfd,...m...). It did not work for me. Is it possible to communicate the server directly to client?
I need you help badly. I will thank you in advance for any help you can give.
Regards,
Allan
problems in multithreaded send()/recv() using tcp socket
On May 24th, 2007 arslan (not verified) says:
i am implementing a multithread ftp server in linux which will work like a download accelerator.
discription:
when a client connects to server ,four tcp connection sockets are established between a client and the server using four threads.it means that 4 thread are on server side ,each sending 1/4th of the file ,each thread using single send(),and four threads on client side, each reciveing 1/4th of the file,each thread using single recv(). file is broken down in four parts.using for loop, each part are sent in the chunks of 1024 bytes append with their seqeunce number .similarly the file is recieved by four threads ,each thread using for loop and in the for loop ä single recv() is used and recieved data in written on single file.
Problem
the server sends the complete file correctly and exits the thread function correctly , the connections are not closed yet .but the client doesnot revcive all the packets and one of the thread on clients side gets stuck.
where is the problem? is it the flow control? but doesnot tcp sockets take care flow and error control???
if I add two consecutive send() commands after the one recv()in each thread of client and in these two send() i send dummy packets. and on server side i add two recv() after the one send()in each thread ........my problem is kind of solved but delayed is increased considerably and this is not good solution....i want to know what the exact problem is , the reason behind it,and its solution tooooo
how to pass structures in sockets
On February 21st, 2007 narsi (not verified) says:
hi,
Could you please help in socket programming.
how to pass structures in sockets.
Thanks & Regards,
Narsi.
Need Help!!!!
On November 16th, 2006 Sunny (not verified) says:
I have a question How can I detect that the connect is lost by just modifier tcp server.
Right now I use timeout to detect that but that make me to send and recv data all the time.
If any one know how do that please tell me and is it have any article about this please tell me
Thank you
Weird Problem
On November 8th, 2006 brad.maillist (not verified) says:
Hello,
let me start off by saying that I found your article to be very helpful. I am new to this so forgive my question, but I have copied out the server code and I am able to compile it using gcc v4.1.0 on Suse 10.1 but when I try to run the code, i get a segmentation fault around the line where argv[1] is assigned to port. I cannot for the life of me figure this out since all the documentation I have read implies that there should not be a problem here.
Thanks for your help
Weird solution
On April 25th, 2007 NeoKuaho (not verified) says:
I can only imagine that when you run the program,
you just enter the program (Server) name and hit enter...?
on the command line, you must also provide a port number since the program is expecting it.
Remember, argv[0] //Is the program's name
argv[1]// is the first parameter after the program name.
try : server 3009
I hope this helps.
Im not the author but if I
On November 30th, 2006 Anonymous (not verified) says:
Im not the author but if I were you I would verify that the port
is being passed through "atoi" before being assigned to the port variable...
if that is the case then I would test out all arguments before the tcp stuff is called to verify how the arguments are being passed to the program.
I would also check to see if you need to use "htons" on the port because of "endian" problems. i.e. x86 integers are stored lsb -> msb whereas most other processors use msb -> lsb format...
Regarding Difference between TCP / UDP
On July 31st, 2006 Sudhir (not verified) says:
hi
i have a query regarding the difference between TCP & UDP from developers pint of view at coding level how can we diffentiate between TCP & UDP if both use Socket,bind,listen,send recv
at the transport layer how does it diffentiate between TCP & UDP
Thankxx in advance
Sudhir
Regarding Difference between TCP / UDP
On April 12th, 2007 Chandra (not verified) says:
Hi,
The diffrence lies with the connect() system call.In client side for TCP the connect() system call is used for reliable connection,but there is no such type of call in UDP side, as it is connection less.
Thanks
errata for Listing 4
On May 29th, 2006 Ivan_Griffin (not verified) says:
Hi,
Thanks to "StratBoy SixtyOne" for point out a typo in one of the file listings to me (6 years after it was written - great to see people still interested in the article and code).
The clientLength parameter should be set to sizeof(clientName) before the recvfrom() call.
Best Regards,
Ivan.
How to get the best route
On November 6th, 2006 Pal (not verified) says:
Pls help me figure out how to programmatically find the best route to a destination in Linux C.
Thanks in advance
Pal
help on socket programming
On April 18th, 2006 vikram (not verified) says:
Hi ,
Could you please help in solving following problem :
1. My Application (huge open source !!1 )is having 2 threads having 2 different UDP sockets , bind on two different ports.( Lets give them name port1 and port2)
2. port1 can send and receive pkts from any device
3. How ever If port2 is in communication with some other device(sending receiving pkts) , and If any other client sends packet to this port2, It got ICMP unrechable .
Why this is happening ? i could not found in code any setting which will cause this to happen . I want to accept packets from source other then
those whom sending to. ( Packet are reaching till devic but discarded with ICMP unreachable)
Thanks in Advance
best regards
Vikram
The Icmp unreachable is
On August 3rd, 2006 Lolo (not verified) says:
The Icmp unreachable is written by the UDP stack.
This happens when no prog are listenning on the port.
May be, your prog is not listenning anymore when it accepts a communication.
This is a pretty good stuff
On February 20th, 2006 Narenderan Perumal (not verified) says:
This is a pretty good stuff to start with socket programming.
Thanks
~Naren
Re: Linux Network Programming, Part 1
On May 15th, 2004 Anonymous says:
Somebody should get with the times.
http://www.linuxjournal.com/modules.php?op=modload&name=NS-lj-issues/issue46&file=2333s2
In this page, you reciprocate the information provided in the Linux
manual page for the listen(2) syscall, stating that BSD *still*
limits this value to 5. It's limited to whatever SOMAXCONN is
set to (indeed, somaxconn, which is a static int initialized to
SOMAXCONN). In FreeBSD, this value is 128 by default.
Yeah! Way to stick it to the
On August 23rd, 2005 Anonymous (not verified) says:
Yeah! Way to stick it to the authors of an article published SIX YEARS BEFORE YOU COMMENTED. You're so much better than they are for "getting with the times!" We bow before you!
Re: Linux Network Programming, Part 1
On July 25th, 2003 Anonymous says:
I'd love to see a simple demonstration like this on how to use threads instead of processes. Are they much more complicated?
Re: Linux Network Programming, Part 1
On October 20th, 2003 Anonymous says:
The answer to that (like most things in computer science) is yes and no. It depends on how much you get into sharing memory (or other resources) between threads. Multithreading itself is not a dificult topic... it is the topic of synchronization that is a little difficult... however, the two are very closely related.
If you are interested in learning about threads do a search on the pthreads library. There is an implementation for windows as well as most unix flavors including linux.
Re: Linux Network Programming, Part 1
On July 30th, 2003 Anonymous says:
what an article?great,excellent to read this.thanks for the author...helped me a lot but one prob..explaining each and every line of the program thru comment statements would be helpful to understand the concepts fully thanks
anand hariharan
Re: Linux Network Programming, Part 1
On June 5th, 2003 Anonymous says:
Good article, well written. A nice easy way to learn sockets. Are there more of these articles by the same authors?
Re: Linux Network Programming, Part 1
On July 21st, 2002 Anonymous says:
I wonder if there is anywhere that you can find a tutorial on handling ICMP. that would be nice.
Re: Linux Network Programming, Part 1
On April 8th, 2002 Anonymous says:
Very helpful article. I am converting for MS Sockets and this re-enforces how similar (identical!) they are. Thank you.
Re: Linux Network Programming, Part 1
On April 14th, 2002 Anonymous says:
IIRC Microsoft lifted the BSD implementation verbatim. I think it even shows up somewhere in the system during bootup or something.
Great tutorial. Thx. I
On February 7th, 2006 Nazgob (not verified) says:
Great tutorial. Thx. I recomment also Beej tutorial.
REQ:multiple client - server communication
On November 3rd, 2006 Anonymous (not verified) says:
good explanation for starters,
i have a questio,
how does the server able to maintain the communication between the multiple clients?how does the server identifies that this particular message have come from this particular client only?
help me out!