DWise1's Sockets Programming Pages

Working with Sockets

Introduction

Network programming is the writing of programs that will communicate over a network with programs running another computer.
Sockets programming offers a standardized way to write these programs.

The Sockets programming model, AKA "Berkeley Sockets", consists of a set of data structures, predefined constants, and functions that perform system calls.
In effect, sockets programming provides the low-level I/O that enables applications to each other across networks and to send and receive blocks of data between each other.
Most of TCP/IP's plethora of protocols operate on the application level, whereas sockets programming only deals with two protocols: tcp and udp.
The complex, innovative, and interesting part of a network program lies in the application's protocol, whereas the sockets part of the application is fairly standard and straight-forward and normally hardly changes at all from one application to the next.
Indeed, in his book, Effective TCP/IP Programming: 44 Tips to Improve Your Network Programs, Jon Snader recommends that you create a library of skeleton sockets code that you can then reuse in all of your network programs (Tip #4).

Although it originated as a TCP/IP programming model, socket programming is flexible enough to be used for other protocol families, including UNIX domain, IPX/SPX, XEROX NS, X.25, SNA, DECnet, AppleTalk, and NetBEUI.
And although it was originally written in C on UNIX, it has been ported to several other programming languages and operating systems; e.g., Perl, Visual BASIC, Delphi, Java, Python, Windows, and LabView.
From what I've seen, the concepts and most of the function names are the same, so much of what you learn in one development environment should be transferable to the others.

My approach on this page is in C and mainly under Linux.
On my Winsock Page I discuss how to apply the information on this page to Windows.

Please note that in the function descriptions given on this page, only the header files for UNIX/Linux are given.
If you are developing under Windows and hence are using Winsock, then in each case replace those header files with either "winsock.h" or "winsock2.h".

[both]Create a socket with socket().
This creates a socket file descriptor (in UNIX/Linux) or handle (Windows) that is required by most of the sockets functions and that the operating system needs to perform network operations.

[server]Bind the socket to a port with bind().
This uses the sockaddr_in data structure to bind the server's socket to a pre-determined known port so that the client will know which port to connect to.
This step could be skipped in which case the operating system would automatically pick a port to bind the socket to, but there's no way to predict which port that will be.

[server]Listen for a connection with listen().
This changes the server's socket into a special type, a "listening socket", that waits for clients to attempt to make a connection.
It can serve no other purpose after this point.
This function will be called once and only once for every listening socket in the server.
This is only used in the tcp protocol.

[client]Connect to a Server with connect().
This uses the sockaddr_in data structure to attempt to connect to the server whose address is in sockaddr_in.
This is only used in the tcp protocol.

[server]Accept a Connection with accept().
This accepts the connection that a client is attempting and returns a sockaddr_in data structure containing the client's address and a new socket that is connected to that client and with which all communication with the client is to be performed.
This is only used in the tcp protocol.

Before we can use a socket, we need to create it.
When we create a socket, the system gives us a "socket handle", called a "descriptor" in UNIX/Linux, which is an integer value that the operating system uses to access the actual socket.
In every function we perform on that socket, we need to provide that socket handle in order to identify which socket to use.
In WinSock this handle is of a "special" type called SOCKET, which incidentally is an unsigned integer, and which can take on values that UNIX/Linux socket descriptors cannot.

So, in order to create a socket, you need to specify the protocol family (PF_INET in our case), the protocol (tcp or udp with the pre-defined constants IPPROTO_TCP and IPPROTO_UDP, respectively), and the "socket type" (SOCK_STREAM for tcp and SOCK_DGRAM for udp).

You create a socket with the function call:

socket()
Creates a socket

#include "sys/types.h"
#include "sys/socket.h"

int socket(int protocol_family, int type, int protocol);

protocol_family -- for our purposes, always PF_INET

type -- Type of socket (SOCK_STREAM or SOCK_DGRAM)

protocol -- Socket protocol (IPPROTO_TCP or IPPROTO_UDP)

On success, socket() returns the descriptor of the new socket.
On failure, it returns -1 and the error is in errno.

NOTE: the type and protocol fields must agree. In your code, this function call will always be:

As you can see, the two fields of address data (two bytes each) plus the 8 bytes of padding make up the full 14 bytes in the sa_data field of sockaddr.

By the way, the reason that in_addr is struct is because it's actually a union (a feature in the C language that enables you to view the same location in memory as different data types, in this case as one unsigned long, as two unsigned shorts, or as four individual bytes.
However, the other definitions are only of historical interest and are no longer put to any practical use.
In all the sockets code I've seen, we only use the unsigned long s_addr declaration and so in many references that's all that's ever listed, even though you will see the union in the header file.

Actually Setting the Address

Given an IP address and a port number, here is how you stuff them into a sockaddr_in structure.
This is most commonly done in in-line code, but I'm presenting it as a function to also give the data declarations:

It is a good idea to zero out the address structure before you start filling it, hence the call to memset().
Some systems also have a function called bzero() that does the same thing.

In our examples, we always set the address family to AF_INET.

inet_addr() converts the dotted-decimal string to the binary address.
Please note that the return value of inet_addr() is already in network byte order.

The port needs to be converted from host byte order to network order (I personally got bitten by this one).
This byte-order conversion is performed by four built-in functions for host-to-network (short int: htons(); long int: htonl()) and for network-to-host (short int: ntohs(); long int: ntohl()).

Curiously, the sin_family field does not need to be converted to network byte order.
I just realized that and haven't figured it out yet.

inet_addr()
Converts a dotted-decimal IP address from a string to an unsigned long

Caveat Programmator -- Replace inet_addr with inet_aton

I normally use inet_addr because that's what I had learned.
However, that function is considered to be obsolete and it is advised against using it in new programs.

Here's the reason: to indicate failure, inet_addr returns INADDR_NONE, which is defined as 0xffffffff.
However, that is also the valid return value for "255.255.255.255", which is commonly used as the universal broadcast address.
Therefore, if inet_addr returns INADDR_NONE, you don't know whether it had failed or it had successfully converted "255.255.255.255".

It is advised to use inet_aton ("ASCII to number") instead of inet_addr.
inet_aton removes any ambiguity by separating the error indication, which it returns as the function value,
from the binary IP address, which it returns through a pointer parameter.

inet_aton()
Converts a dotted-decimal IP address from a string to a struct in_addr

inp -- Pointer to the in_addr struct that will receive the binary IP address in network byte order

inet_aton returns nonzero if the address is valid, zero if not.

However, inet_aton is not a "pin-for-pin" replacement for inet_addr, because it
does not return the IP address per se, but rather it stuffs the IP address into a in_addr struct
that is provided by the calling function.
Here is how we would rewrite the StuffSockAddr_in function to use inet_aton:

Another nice thing about inet_aton is that it offers nice symmetry with the reverse function, inet_ntoa
(number to ASCII),
that converts a 32-bit binary IP address to a dotted-decimal string and which takes an in_addr struct:

inet_ntoa returns the dotted-decimal string.
The string is returned in a statically
allocated buffer, which subsequent calls will overwrite, so copy it if you want to save it for future use.

Miscellaneous Addressing Topics

An application in a multihomed host (has more than one IP address), can specify which ethernet interface to use or else can work with any of them.
To work with any of its ethernet interfaces, there is a special pre-defined address, INADDR_ANY, which would be used thus:

addr->sin_addr.s_addr = INADDR_ANY; /* Any incoming interface */

Please note that you do not need to convert it to network byte order.
Actually, it's defined as an unsigned long with a value of zero, which is always the same no matter how you order its bytes.

Now that we have created a socket, we need to associate it with a port. This is called "binding".

Actually, if we just start to use a socket without having called bind() first, the system automatically binds it to a randomly chosen port.
This works just fine for a client, but not for a server.
In order for a remote client to connect to a server, that client must know ahead of time which port the server is bound to.
This means that a server must bind explicitly to a specific port.
However, there can also be a valid need for a client to bind to a specific port, such as in a peer-to-peer application or in a broadcast client.

localAddress -- the address structure containing the IP address of the network interface and the port number.
The special address ANY_ADDR can be used to allow the connection to be made on any of the host's intefaces.

addresslength -- the size in bytes of the sockaddr structure

On success, bind() returns a zero.
On failure, it returns -1 and the error is in errno. Usually EADDRINUSE.

This function is used for stream/TCP sockets only
and is used in conjunction with accept().

In a TCP connection, one application, the server, binds to a well-known port and waits for a client to try to connect to it. This is called "listening" and it is performed by calling the listen() function.

listen() changes the socket to a special type whose only function is to process new attempts to connect on that port.
It must already be bound to a local port; ie, bind() must have already been called.

When a client's connection request comes in, if the queue has not been filled (queue length is set with the "backlog" parameter), then the new connection request is placed in the queue.
If the queue is full, then the new connection request will be rejected, which will result in an error in the client application.
Queued connections are removed from the queue and completed with the accept() function, which also creates a new socket for communicating with the client.

listen() does not block. It returns immediately from the function call.

listen()
Sets a socket to listen for incoming connectionstcp sockets only

#include "sys/types.h"
#include "sys/socket.h"

int listen(int socket, int backlog);

socket -- Socket (returned from socket())

backlog -- Maximum number of new connections (sockets) to allow to wait

On success, listen() returns 0
On failure, it returns -1 and the error is in errno.

Requests a connection to a specified port at a specified peer address.
While it's most commonly used with the tcp protocol, it can also be used with udp.
In the tcp protocol, success means that a TCP connection has been established.
Failure can occur for any of several possible reasons; eg, failure to find the server system,
no server was listening on that port, the server rejected the connection (eg, because the queue was full).
In case of failure, you will need to examine the error code to determine the reason.
Calling connect() initiates the "Triple Handshake".

If the client socket had not already been bound to a port with bind(),
then connect() will bind it to a randomly assigned port and fill in its local address information as well as the peer's information.
After this point, you may read the socket's local address information with getsockname() and its peer address information with getpeername().

This function is used for stream/TCP sockets only
and is used in conjunction with listen().

accept() completes the process of establishing a TCP connection between the server and the client.
It removes the first waiting entry in the listen queue, creates a new socket for it, and fills in that new socket's
address information with both the local and the remote address information, which are accessible
through getsockname() and getpeername() respectively.

However, if there is no pending connection waiting in the queue, then accept() won't return until one does come in.
This means that accept()blocks; it's the first function we've considered here that does block.
This means that that thread of code cannot continue until accept() returns.
If the program only consists of a single thread of code, then the entire program "freezes" until a connection is made.
This may be acceptable in the most simple of servers that will only handle one client at a time, but it is unacceptable in most serious applications.

Blocking is a non-trivial problem and a very real issue in sockets programming, that has led to a number of solutions, some of which are system-dependent.
Some solutions include using multiple processes or threads (one for each blocking socket), changing the sockets to non-blocking mode which requires special handling, and using the select() function (to test whether there's anything for the blocking function call to process before actually calling it).
I describe and discuss these in my "Dealing With and Getting Around Blocking Sockets" topic page.

And, obviously, if listen() had not been called to create a listening socket, then there would be no queue for waiting connections.
In that case, accept() would return with an error.

On success, accept() returns the new socket descriptor.
On failure, it returns -1 (SOCKET_ERROR in Winsock) and the error is in errno.

Note:

There is a basic rule in sockets programming that you cannot use the same port for two different things at the same time. That only makes sense.
If one process is listening on a port for incoming messages, another process cannot use the same port to send messages nor to also start listening.

That rule seems very straight-forward, but I got confused for a long time because of a misunderstanding about the new socket that accept() returns.
I had associated ports and sockets too closely together, so I interpreted that new socket as also requiring a new port.
Needless to say, I was very surprised when our Linux-server instructor ran the netstat utility on a web server and I saw every single client connecting to it through TCP port 80.

"Each socket is uniquely identified by a pair of addresses: the local address and the peer's address. This is important to remember, because we must be careful not to associate a socket too closely with a local port. There is a cardinal rule that a given socket can only be used for one connection at a time. But if we run a server, say a web server on TCP port 80, and ten clients connect to us, then we will see that our local TCP port 80 is involved in each of those ten connections. That appears to violate that cardinal rule. However, what is really happening is that each of those are different sockets, because each of them has a different peer address. We are not using the same socket ten times over, but rather we are using ten different sockets. There is no actual conflict nor violation of the rule."

So bear in mind that the new socket returned by accept() will be on the same local port as the listening socket;
you can verify that with a call to getsockname().
But it is still a different socket.

Sends the bytes contained in the buffer over the given connected socket.
The actual format or contents of the data buffer are immaterial to the function.

When sent over a TCP socket, the size of the data can exceed the maximum size for a data packet.
The tcp protocol will simply break that data up into multiple packets that get sent separately.
Then the peer receiving the packets will put them back together again to reconstruct the original block of data.
It should be noted that every send() will have a single corresponding recv() at the receiving peer.
Even though the send() resulted in multiple packets, it appears to the receiving peer application as if that data had been sent in a single packet.

send()
Sends a message on a connected socketconnected sockets only; normally TCP, but could be UDP

The opposite number of send(), recv() takes the data received
over the connected socket and copies to the buffer address provided to it for up to the maximum number specified.

Its return value is the number of bytes received, or -1 if there was an error.
However, if the connection closes, then the return value will be zero.

recv()blocks until there is at least one byte of data to be read or else the connection closes.
Therefore, this is another part of the program that will need to address the blocking problem.

recv()
Receives a message on a connected socketconnected sockets only; normally TCP, but could be UDPBlocks

#include "sys/types.h"
#include "sys/socket.h"

int recv(int socket, void *rcvbuffer, int bufferlength, int flags);

socket -- Socket (must be in connected state)

rcvbuffer -- Where to put the data

bufferlength -- Maximum number of bytes to put in buffer

flags -- Control flags, 0 in most cases

On success, recv() returns the number of bytes received.
On failure, it returns -1 and the error is in errno.

One thing to keep in mind is that the packet that the other host had sent could have been fragmented into several smaller packets for transmission, which were then reassembled as they were received.
That means that that when you start reading in data via recv(), you might have not yet received the entire packet.
You may need to call recv() multiple times to read everything in.
How do you know whether you've received the entire packet?
That would part of the application protocol.
Just something to keep in mind.

The opposite number of sendto(), recvfrom() takes the data received
in a datagram and copies it to the buffer address provided to it for up to the maximum number specified.
It also loads the sender's address information into the address structure passed to it.

It is very similar to recv(), except that the sender's address is also returned.
And like recv(), recvfrom() also blocks until there is at least one byte of data to be read.
Therefore, this is another part of the program that will need to address the blocking problem.

And it also differs from recv() in that you are assured of having received the either packet, by the very nature of UDP.

recvfrom()
Receives a datagram
normally used with UDP datagrams, but could also be used with a TCP connected socketBlocks

shutdown() is used to close the connection in an orderly and graceful manner (yet another hot topic).
This would normally only be done with TCP sockets, but it could also be done with UDP sockets that were connected.

Basically, with the parameters you indicate whether you are done sending or done receiving or done with both.
With that, you can indicate your intention to terminate the session and close the connection.
And with the proper choice of parameter, you can ensure that no data will be lost in the process.

Normally, this is the shutdown procedure for a TCP application:

You will start by signalling that you will not send any more packets by calling shutdown(1).

On success, shutdown() returns zero.
On failure, it returns -1 and the error is in errno.

WinSock's handling of the shutdown() function and of the shutdown process in general is considered as not being as graceful as in UNIX and there are warnings that it can cause unexpected errors.
Be mindful of this when things aren't working exactly as you expect them to.

For example, in UNIX recv() returns a zero when the connection has been broken.
Winsock is intended to do the same thing and sometimes does, but it could also return a -1 with an error of WSAECONNRESET, WSAECONNABORTED or WSAESHUTDOWN.
Similarly, in UNIX send() will return a -1 and a EPIPE error, whereas in Winsock it could behave exactly as recv() by returning either a zero or a -1 with the same errors as recv().

So if you're working with Winsock, you will need to become familiar with all of its peculiarities.

Just like closing a file (which is what you are doing on UNIX/Linux), close() closes the socket, releases that resource, and makes the socket cease to exist.
All operations still pending on the socket, including any communications, are immediately terminated.
Any subsequent attempts to use that socket (except to create a new one) will result in error.
That socket is history. That's all she wrote.

close()
Closes the socket

#include "unistd.h"

int close(int socket);

socket -- The socket to be closed

On success, close() returns 0.
On failure, it returns -1 and the error is in errno.

The Winsock Way

Yet again, Winsock is a bit different.
While almost everything on UNIX is a file, including sockets, Windows has not followed that philosophy.
So while any kind of file in UNIX can be closed with the same close() function,
in Windows system objects such as a SOCKET must be closed with a special function that handles that specific kind of system object.

Therefore, in Winsock you must use a special function, closesocket().
It works pretty much the same as the close() function on UNIX,
but the effects of calling closesocket() can become quite involved depending on what the socket's options were set to, so you should read
the help page on Microsoft's MSDN site for more detailed information
(that page also links to the Winsock error codes).