Description:
Sockets are an inter-process network communication implementation using a Internet Protocol (IP) stack on an Ethernet transport.
Sockets are language and protocol independent and available to "C", Perl, Python, Ruby and Java (and more) programmers.
The "C" language BSD API is used on Linux, all popular variants of Unix, Microsoft Windows (NT,2000,XP,... and later) and even embedded OSs like VxWorks. It is by far the most popular implementation of inter-process network communication.
Sockets allow one process to communicate with another whether it is local on the same computer system or remote over the network.
Many other higher level protocols are built upon sockets technology.
The sockets API provides many configuration options so we will try and cover the socket API components and then give examples of a few implementations.
It would be very difficult to cover all variations of its use.
Sockets utilize the following standard protocols:
Protocol | Description |
IP | Internet Protocol provides network routing using IP addressing eg 192.168.1.204 |
UDP | User Datagram Protocol - IP with ports to distinguish among processes running on same host. No data verification. |
TCP | Transmission Control Protocol - IP with ports to distinguish among processes running on same host. Connection oriented, stream transfer, full duplex, reliable with data verification. |
BSD socket API:
Typically one configures a socket server to which a socket client may attach and communicate.
The IP protocol layer will also require that the domain name or IP addresses of the communicating processes be made known as well.
Within the IP protocol it is also important to provide the mechanism used: TCP or UDP.
The BSD is a "C" programming API. Examples shown are compiled using the GNU C++ compiler on Linux:
Basic steps in using a socket:
- Socket include files:
Include File | Description |
sys/types.h | Types used in sys/socket.h and netinet/in.h |
netinet/in.h | Internet domain address structures and functions |
netinet/tcp.h | Socket option macro definitions, TCP headers, enums, etc |
sys/socket.h | Structures and functions used for socket API.i accept(), bind(), connect(), listen(), recv(), send(), setsockopt(), shutdown(), etc ... |
netdb.h | Used for domain/DNS hostname lookup |
sys/select.h | Used by the select(), pselect() functions and defines FD_CLR, FD_ISSET, FD_SET, FD_ZERO macros |
sys/time.h | select() uses argument of type struct timeval and pselect() uses struct timespec defined by this include file. |
arpa/inet.h | Definitions for internet operations. Prototypes functions such as htonl(), htons(), ntohl(), ntohs(), inet_addr(), inet_ntoa(), etc ... |
unistd.h | Defines constants and types |
errno.h | Defines sytem error numbers |
- Create the socket instance:
Open a socket using TCP: Basic declarations and call to "socket".
02 | #include <sys/types.h> // Types used in sys/socket.h and netinet/in.h |
03 | #include <netinet/in.h> // Internet domain address structures and functions |
04 | #include <sys/socket.h> // Structures and functions used for socket API |
05 | #include <netdb.h> // Used for domain/DNS hostname lookup |
18 | if ((socketHandle = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) |
Socket function prototype:
int socketHandle = socket(int socket_family, int socket_type, int protocol);
Choose socket communications family/domain:
- Internet IPV4: AF_INET
- Internet IPV6: AF_INET6
- Unix path name (communicating processes are on the same system): AF_UNIX
Choose socket type:
- TCP: SOCK_STREAM
- UDP: SOCK_DGRAM
- Raw protocol at network layer: SOCK_RAW
Choose socket protocol: (See /etc/protocols)
- Internet Protocol (IP): 0 or IPPROTO_IP
- ICMP: 1
- ...
Also see:
- Configure the socket as a client or server:
Comparison of sequence of BSD API calls:
TCP | UDP |
Socket Server | Socket Client | Socket Server | Socket Client |
socket() | socket() | socket() | socket() |
bind() | gethostbyname() | bind() |
|
listen() |
|
|
|
accept() | connect() |
|
|
recv()/send() | recv()/send() | recvfrom()/sendto() | recvfrom()/sendto() |
close() | close() | close() | close() |
This is specific to whether the application is a socket client or a socket server.
- Socket server: TCP
- bind(): bind the socket to a local socket address. This assigns a name to the socket.
- listen(): listen for connections on a socket created with "socket()" and "bind()" and accept incoming connections. This is used for TCP and not UDP.
Zero is returned on success.
- accept(): accept a connection on a socket. Accept the first
connection request on the queue of pending connections, create a new
connected socket with mostly the same properties as defined by the call to "socket()", and allocate a new file descriptor for the socket, which is returned.
The newly created socket is no longer in the listening state.
Note this call blocks until a client connects.
03 | #define MAXHOSTNAME 256 |
07 | struct sockaddr_in socketAddress; |
08 | char sysHost[MAXHOSTNAME+1]; |
09 | struct hostNamePtr *hPtr; |
10 | int portNumber = 8080; |
12 | bzero(&socketInfo, sizeof (sockaddr_in)); |
16 | gethostname(sysHost, MAXHOSTNAME); |
17 | if ((hPtr = gethostbyname(sysHost)) == NULL) |
19 | cerr << "System hostname misconfigured." << endl; |
25 | socketInfo.sin_family = AF_INET; |
29 | socketInfo.sin_addr.s_addr = htonl(INADDR_ANY); |
30 | socketInfo.sin_port = htons(portNumber); |
34 | if ( bind(socketHandle, ( struct sockaddr *) &socketInfo, sizeof ( struct sockaddr_in)) < 0) |
41 | listen(socketHandle, 1); |
44 | if ( (socketConnection = accept(socketHandle, NULL, NULL)) < 0) |
Socket functions:
- bind():
Function prototype:
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
Bind arguments:
- int sockfd: Socket file descriptor. Returned by call to "socket".
- struct sockaddr: Socket information structure
- socklen_t addrlen: Size of structure
- Returns 0: Sucess, -1: Failure and errno may be set.
Also see the bind man page
- listen():
Function prototype:
int listen(int s, int backlog);
Listen arguments:
- int s: Socket file descriptor. Returned by call to "socket". Identifies a bound but unconnected socket.
- int backlog: Set maximum length of the queue of pending connections for the listening socket. A reasonable value is 10.
Actual maximum permissible: SOMAXCONN
Example: int iret = listen(socketHandle, SOMAXCONN);
The include file sys/socket.h will include
/usr/include/bits/socket.h which defines the default value for SOMAXCONN as 128.
The actual value set for the operating system: cat /proc/sys/net/core/somaxconn
In kernels before 2.4.25, this limit was a hard coded value and thus would require a kernel recompile with the SOMAXCONN value as defined in /usr/include/linux/socket.h
For very heavy server use, modify the system limit in the proc file and set "backlog" to the same value (eg. 512).
- Returns 0: Sucess, -1: Failure and errno may be set.
Also see the listen man page
- accept():
Function prototype:
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
Accept arguments:
- int s: Socket file descriptor. Returned by call to "socket".
- struct sockaddr *addr: Pointer to a sockaddr structure. This structure is filled in with the address of the connecting entity.
- socklen_t *addrlen: initially contains the size of the
structure pointed to by addr; on return it will contain the actual length (in bytes) of the address returned. When addr is NULL nothing is filled in.
- Returns:
- Success: non-negative integer, which is a descriptor of the accepted socket. Argument "addrlen" will have a return value.
- Fail: -1, errno may be set
Also see the accept man page
[Potential Pitfall]: If you get the following message:
bind: Address already in use
The solution is to choose a different port or kill the process which is using the port and creating the conflict.
You may have to be root to see all processes with netstat.
netstat -punta | grep 8080
tcp 0 0 :::8080 :::* LISTEN
- Socket client: TCP
- connect(): initiate a connection with a remote entity on a socket. Zero is returned on success. Support both TCP (SOCK_STREAM) and UDP (SOCK_DGRAM). For SOCK_STREAM, an actual connection is made. For SOCK_DGRAM the address is the address to which datagrams are sent and received.
04 | struct sockaddr_in remoteSocketInfo; |
07 | char *remoteHost= "dev.megacorp.com" ; |
10 | bzero(&remoteSocketInfo, sizeof (sockaddr_in)); |
14 | if ((hPtr = gethostbyname(remoteHost)) == NULL) |
16 | cerr << "System DNS name resolution not configured properly." << endl; |
17 | cerr << "Error number: " << ECONNREFUSED << endl; |
23 | memcpy (( char *)&remoteSocketInfo.sin_addr, hPtr->h_addr, hPtr->h_length); |
24 | remoteSocketInfo.sin_family = AF_INET; |
25 | remoteSocketInfo.sin_port = htons((u_short)portNumber); |
27 | if ( (connect(socketHandle, ( struct sockaddr *)&remoteSocketInfo, sizeof (sockaddr_in)) < 0) |
Connect function prototype:
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
Connect arguments: (Same as server's bind() arguments)
- int sockfd: Socket file descriptor. Returned by call to "socket".
- struct sockaddr: Socket information structure
- socklen_t addrlen: Size of structure
- Returns 0: Sucess, -1: Failure and errno may be set.
Zero is returned upon success and on error, -1 and errno is set appropriately.
Also see the connect man page
The sockaddr_in data structure: /usr/include/linux/in.h
07 | #define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */ |
09 | sa_family_t sin_family; |
10 | unsigned short int sin_port; |
11 | struct in_addr sin_addr; |
14 | unsigned char __pad[__SOCK_SIZE__ - sizeof ( short int ) - |
15 | sizeof (unsigned short int ) - sizeof ( struct in_addr)]; |
Note:
- IP addresses: Note the specific IP address could be specified:
#include <arpa/inet.h> // IP from string conversion
socketInfo.sin_addr.s_addr = inet_addr("127.0.0.1");
cout << inet_ntoa(socketInfo.sin_addr) << endl;
or bind to all network interfaces available:
socketInfo.sin_addr.s_addr = htonl(INADDR_ANY);
- Port numbers: Note the specific port can be specified:
socketInfo.sin_port = htons(8080);
cout << ntohs(socketInfo.sin_port) << endl;
or let the system define one for you:
socketInfo.sin_port = htons(INADDR_ANY);
- Read from or write to socket: TCP/UDP
Use send() and recv(), or write() and read(), or sendto() and recvfrom() to read/write to/from a socket.
- TCP recv() or UDP recvfrom(): receive a message from a socket
07 | if (iSocketType == SOCK_STREAM) |
09 | rc = recv(socketHandle, ( char *) _pcMessage, ( int ) _iMessageLength, 0); |
12 | cerr << "ERROR! Socket closed" << endl; |
16 | cerr << "ERROR! Socket error" << endl; |
20 | else if (iSocketType == SOCK_DGRAM) |
23 | struct sockaddr_in stReceiveAddr; |
24 | iLength = ( int ) sizeof ( struct sockaddr_in); |
25 | memset (( void *) &stReceiveAddr, 0, iLength); |
27 | rc = recvfrom(socketHandle, ( char *) _pcMessage, ( int ) _iMessageLength, 0, |
28 | ( struct sockaddr *) &stReceiveAddr, (socklen_t*) &iLength)) |
31 | cerr << "ERROR! Socket closed" << endl; |
35 | cerr << "ERROR! Socket error" << endl; |
40 | pcIpAddress = inet_ntoa(stReceiveAddr.sin_addr); |
41 | shPort = ntohs(stReceiveAddr.sin_port); |
43 | cout << "Socket Received: " << _iNumRead << " bytes from " |
44 | << pcIpAddress << ":" << shPort << endl; |
- read(): read a specific number of bytes from a file descriptor
09 | while (bcount < numToRead) |
12 | if ( (rc = read(socketHandle, buf, numToRead - count)) > 0); |
23 | cout << "Number of bytes read: " << count << endl; |
24 | cout << "Received: " << buf << endl; |
- send(): send a message from a socket. Used only when in a connected state. The only difference between send and write is the presence of flags. With zero flags parameter, send is equivalent to write.
08 | strcpy (buf, "Message to send" ); |
13 | send(socketHandle, buf, strlen (buf)+1, 0); |
- TCP send() or UDP sendto():
03 | char *pMessage = "message to send" ; |
04 | int iMessageLength = 16; |
10 | if (iSocketType == SOCK_STREAM) |
12 | if ((iBytesSent = send(socketHandle, ( char *) pMessage, ( int ) iMessageLength, 0)) < 0 ) |
14 | cerr << "Send failed with error " << errno << endl; |
18 | else if (iSocketType == SOCK_DGRAM) |
20 | if ((iBytesSent = sendto(socketHandle, ( char *) pMessage, |
23 | ( struct sockaddr*) pSendAddress, |
24 | ( int ) sizeof ( struct sockaddr_in))) < 0 ) |
26 | cerr << "Sendto failed with error " << errno << endl; |
- Close the socket when done: TCP/UDP
This is the "C" library function to close a file descriptor.
Returns zero on success, or -1 if an error occurred.
Socket Server:
Simple TCP Socket Server:
File:
server.cpp
03 | #include <sys/socket.h> |
08 | #define MAXHOSTNAME 256 |
13 | struct sockaddr_in socketInfo; |
14 | char sysHost[MAXHOSTNAME+1]; |
17 | int portNumber = 8080; |
19 | bzero(&socketInfo, sizeof (sockaddr_in)); |
23 | gethostname(sysHost, MAXHOSTNAME); |
24 | if ((hPtr = gethostbyname(sysHost)) == NULL) |
26 | cerr << "System hostname misconfigured." << endl; |
32 | if ((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
40 | socketInfo.sin_family = AF_INET; |
41 | socketInfo.sin_addr.s_addr = htonl(INADDR_ANY); |
42 | socketInfo.sin_port = htons(portNumber); |
46 | if ( bind(socketHandle, ( struct sockaddr *) &socketInfo, sizeof (socketInfo)) < 0) |
53 | listen(socketHandle, 1); |
56 | if ( (socketConnection = accept(socketHandle, NULL, NULL)) < 0) |
70 | rc = recv(socketConnection, buf, 512, 0); |
73 | cout << "Number of bytes read: " << rc << endl; |
74 | cout << "Received: " << buf << endl; |
Forking Socket Server:
In order to accept connections while processing previous connections, use fork() to handle each connection.
Use establish() and get_connection() to allow multiple connections.
File:
serverFork.cpp
03 | #include <sys/socket.h> |
12 | #include <netinet/in.h> |
13 | #define MAXHOSTNAME 256 |
17 | void handleSig( int signum) |
19 | while (waitpid(-1, NULL, WNOHANG) > 0); |
24 | struct sockaddr_in socketInfo; |
25 | char sysHost[MAXHOSTNAME+1]; |
28 | int portNumber = 8080; |
30 | signal (SIGCHLD, handleSig); |
32 | bzero(&socketInfo, sizeof (sockaddr_in)); |
36 | gethostname(sysHost, MAXHOSTNAME); |
37 | if ((hPtr = gethostbyname(sysHost)) == NULL) |
39 | cerr << "System hostname misconfigured." << endl; |
45 | if ((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
53 | socketInfo.sin_family = AF_INET; |
54 | socketInfo.sin_addr.s_addr = htonl(INADDR_ANY); |
55 | socketInfo.sin_port = htons(portNumber); |
59 | if ( bind(socketHandle, ( struct sockaddr *) &socketInfo, sizeof ( struct sockaddr_in)) < 0) |
66 | listen(socketHandle, 1); |
71 | if ( (socketConnection = accept(socketHandle, NULL, NULL)) < 0) |
74 | if ( errno == EINTR) continue ; |
83 | close(socketConnection); |
90 | close(socketConnection); |
For more on the use of the
fork() function see the YoLinux.com
fork() tutorial.
Socket Client:
Simple Socket TCP client:
File:
client.cpp
04 | #include <sys/socket.h> |
09 | #define MAXHOSTNAME 256 |
14 | struct sockaddr_in remoteSocketInfo; |
17 | const char *remoteHost= "localhost" ; |
18 | int portNumber = 8080; |
20 | bzero(&remoteSocketInfo, sizeof (sockaddr_in)); |
24 | if ((hPtr = gethostbyname(remoteHost)) == NULL) |
26 | cerr << "System DNS name resolution not configured properly." << endl; |
27 | cerr << "Error number: " << ECONNREFUSED << endl; |
33 | if ((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
41 | memcpy (( char *)&remoteSocketInfo.sin_addr, hPtr->h_addr, hPtr->h_length); |
42 | remoteSocketInfo.sin_family = AF_INET; |
43 | remoteSocketInfo.sin_port = htons((u_short)portNumber); |
45 | if (connect(socketHandle, ( struct sockaddr *)&remoteSocketInfo, sizeof (sockaddr_in)) < 0) |
54 | strcpy (buf, "Message to send" ); |
55 | send(socketHandle, buf, strlen (buf)+1, 0); |
Test Simple Client and Server Socket program:
Note that this runs on a single system using "localhost".
Compile the simple client and the simple server:
- g++ server.cpp -o server
- g++ client.cpp -o client
Start the server:
./server
This will block at the "accept()" call and await a connection from a client.
Start the client: ./client
This will connect to the server and write the message "Message to send".
The server will receive the message and write it out.
Note that the above compile/link commands apply to Linux (and Cygwin which includes socket and network libraries in cygwinl.dll).
Other Unix based platforms may require that you explicitly specify the libraries:
- g++ server.cpp -o server -lnsl -socket
- g++ client.cpp -o client -lnsl -socket
Name Resolution and Network Information Lookup:
Network information data lookup for:
- DNS: Name resolution associates IP address to a hostname using DNS (Domain Name System) name servers. Name resolution invokes a series of library routines to query the name servers.
- TCP/IP ports and associated services
- Network Protocols
- Network name information
Function | Description |
gethostname(char *name, size_t len) | returns hostname of local host |
getservbyname(const char *name,const char *proto) | returns a structure of type servent for the given host name and protocol |
gethostbyname(const char *name) | returns a structure of type hostent for the given host name |
getservbyport(int port, const char *proto) | returns a servent structure for the line that matches the port port given in network byte order using protocol proto. If proto is NULL, any protocol will be matched. |
getservent(void) | returns a structure servent containing the broken out fields from the line in /etc/services |
getprotobyname(const char *name) | returns a structure protoent containing the broken out fields from the line in /etc/protocols |
getprotobynumber(int proto) | returns a protoent structure for the line that matches the protocol number |
getprotoent(void) | returns a structure protoent containing the broken out fields from the line in /etc/protocols |
getnetbyname(const char *name) | a structure netent containing the broken out fields from the line in /etc/networks |
getnetbyaddr(long net, int type) | returns a netent structure for the line that matches the network number net of type "type" |
getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) void freeaddrinfo(struct addrinfo *res) | Returns 0 if it succeeds or an error code. Network address and service translation freeaddrinfo() frees the memory that was allocated by getaddrinfo() |
getnetent(void) | returns a structure netent containing the broken out fields from the line in /etc/networks |
Data Structures returned:
hostent defined in <netdb.h>
Lookup of data in
/etc/hosts or from DNS resolution.
servent defined in <netdb.h>
Lookup of data in
/etc/services
protoent defined in <netdb.h>
Lookup of data in
/etc/protocols
netent defined in <netdb.h>
5 | unsigned long int n_net; |
Lookup of data in
/etc/networks
Socket Configuration Options:
Socket options:
One can "get" (read) the current socket options or "set" them to new values.
The default values are obtained from the OS:
Level | Option | Type | Default | Description |
IPPROTO_IP | TCP_NODELAY | int | 0 | Don't delay send to coalesce packets. If set, disable the Nagle algorithm. When not set, data is buffered until there is a sufficient amount to send out, thereby avoiding the frequent sending of small packets, which results in poor utilization of the network. Don't use with TCP_CORK. This option is overridden by TCP_CORK |
IPPROTO_IP | TCP_MAXSEG | int | 536 | Maximum segment size for outgoing TCP packets. TCP will impose its minimum and maximum bounds over the value provided. |
IPPROTO_IP | TCP_CORK | int | 0 | Control sending of partial frames. If set, don't send out partial frames. Not cross platform. |
IPPROTO_IP | TCP_KEEPIDLE | int | 7200 | When the SO_KEEPALIVE option is enabled, TCP probes a connection that has been idle for some amount of time. The default value for this idle period is 2 hours. The TCP_KEEPIDLE option can be used to affect this value for a given socket, and specifies the number of seconds of idle time between keepalive probes. Not cross platform. This option takes an int value, with a range of 1 to 32767. |
IPPROTO_IP | TCP_KEEPINTVL | int | 75 | Specifies the interval between packets that are sent to validate the connection. Not cross platform. |
IPPROTO_IP | TCP_KEEPCNT | int | 9 | When the SO_KEEPALIVE option is enabled, TCP probes a connection that has been idle for some amount of time. If the remote system does not respond to a keepalive probe, TCP retransmits the probe a certain number of times before a connection is considered to be broken. The TCP_KEEPCNT option can be used to affect this value for a given socket, and specifies the maximum number of keepalive probes to be sent. This option takes an int value, with a range of 1 to 32767. Not cross platform. |
IPPROTO_IP | TCP_SYNCNT | int | 5 | Number of SYN retransmits that TCP should send before aborting the attempt to connect. It cannot exceed 255. |
IPPROTO_IP | TCP_LINGER2 | int | 60 | Life time of orphaned FIN-WAIT-2 state. Not to be confused with option SO_LINGER Not cross platform. |
SOL_SOCKET | SO_REUSEADDR | int (bool) | 0 | Allow local address reuse. If a problem is encountered when attempting to bind to a port which has been closed but not released (may take up to 2 minutes as defined by TIME_WAIT). Apply the SO_REUSEADDR socket option to release the resource immediately and to get around the TIME_WAIT state. 0 = disables, 1 = enables |
SOL_SOCKET | SO_REUSEPORT | int (bool) | 0 | This option is AF_INET socket-specific. This option allows multiple processes to share a port. All incoming multicast or broadcast UDP datagrams that are destined for the port are delivered to all sockets that are bound to the port. All processes that share the port must specify this option. 0 = disables, 1 = enables |
SOL_SOCKET | SO_ERROR | int (bool) | 0 | When an error occurs on a socket, set error variable so_error and notify process 0 = disables, 1 = enables |
SOL_SOCKET | SO_BROADCAST | int (bool) | 0 | Permit sending of broadcast datagrams 0 = disables, 1 = enables |
SOL_SOCKET | SO_SNDBUF | int (value) | 16384 | Send buffer size Warning: This may prevent socket operations with platforms other than Linux. |
SOL_SOCKET | SO_RCVBUF | int (value) | 87380 | Receive buffer size Warning: This may prevent socket operations with platforms other than Linux. |
SOL_SOCKET | SO_KEEPALIVE | int (bool) | 0 | Periodically test if connection is alive 0 = disables, 1 = enables |
SOL_SOCKET | SO_SNDTIMEO | timeval (struct) | 0 0 | Set timeout period for socket send. Disable by setting timeval.tv_sec = 0 sec, timeval.tv_usec = 0 usec (default) Affects write() writev() send() sendto() and sendmsg() |
SOL_SOCKET | SO_RCVTIMEO | timeval (struct) | 0 0 | Set timeout period for socket receive. Disable by setting timeval.tv_sec = 0 sec, timeval.tv_usec = 0 usec (default) Affects read() readv() recv() recvfrom() and recvmsg() |
SOL_SOCKET | SO_LINGER | linger (struct) | 0 0 | Specifies how close function will operate for connection protocols (TCP) l_onoff: 0 = disables, 1 = enables l_linger: 0 = unsent data discarded, 1 = close() does not return untill all unsent data is transmitted or remote connection is closed Structure defined in sys/socket.h |
SOL_SOCKET | SO_RCVLOWAT | int (value) | 1 | Specifies number of bytes used as a threshold by select() to consider a socket read ready |
SOL_SOCKET | SO_SNDLOWAT | int (value) | 1 | Specifies number of bytes used as a threshold by select() to consider a socket write ready |
SOL_SOCKET | SO_TYPE | int (value) | undefined | Specifies socket type (e.g., tcp (SOCK_STREAM), udp (SOCK_DGRAM), etc.) For use with getsockopt() only. |
IPPROTO_IP macro defines are found in
/usr/include/netinet/tcp.h
SOL_SOCKET macro defines require
/usr/include/sys/socket.h
For a full list of options see the TCP man page
For a full list of IP options see the IP(7) man page
Function Prototypes:
int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
getsockopt/setsockopt arguments:
- int sockfd: Socket file descriptor. Returned by call to "socket".
- int level: See table above
- int optname: See table above
- void *optval: Pointer to value or data structure
- optlen: Length of "optval"
- Returns 0: Sucess, -1: Failure and errno may be set.
Code to read socket options:
File:
printSocketOptions.c
01 | #include <sys/socket.h> |
02 | #include <netinet/in.h> |
03 | #include <netinet/tcp.h> |
13 | if ((socketHandle = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) |
19 | int iSocketOption = 0; |
20 | int iSocketOptionLen = sizeof ( int );; |
22 | struct linger SocketOptionLinger; |
23 | int iSocketOptionLingerLen = sizeof ( struct linger);; |
25 | getsockopt(socketHandle, IPPROTO_TCP, TCP_NODELAY, ( char *)&iSocketOption, &iSocketOptionLen); |
26 | printf ( "Socket TCP_NODELAY = %d\n" , iSocketOption); |
28 | getsockopt(socketHandle, IPPROTO_TCP, TCP_MAXSEG, ( char *)&iSocketOption, &iSocketOptionLen); |
29 | printf ( "Socket TCP_MAXSEG = %d\n" , iSocketOption); |
31 | getsockopt(socketHandle, IPPROTO_TCP, TCP_CORK, ( char *)&iSocketOption, &iSocketOptionLen); |
32 | printf ( "Socket TCP_CORK = %d\n" , iSocketOption); |
34 | getsockopt(socketHandle, IPPROTO_TCP, TCP_KEEPIDLE, ( char *)&iSocketOption, &iSocketOptionLen); |
35 | printf ( "Socket TCP_KEEPIDLE = %d\n" , iSocketOption); |
37 | getsockopt(socketHandle, IPPROTO_TCP, TCP_KEEPINTVL, ( char *)&iSocketOption, &iSocketOptionLen); |
38 | printf ( "Socket TCP_KEEPINTVL = %d\n" , iSocketOption); |
40 | getsockopt(socketHandle, IPPROTO_TCP, TCP_KEEPCNT, ( char *)&iSocketOption, &iSocketOptionLen); |
41 | printf ( "Socket TCP_KEEPCNT = %d\n" , iSocketOption); |
43 | getsockopt(socketHandle, IPPROTO_TCP, TCP_SYNCNT, ( char *)&iSocketOption, &iSocketOptionLen); |
44 | printf ( "Socket TCP_SYNCNT = %d\n" , iSocketOption); |
46 | getsockopt(socketHandle, IPPROTO_TCP, TCP_LINGER2, ( char *)&iSocketOption, &iSocketOptionLen); |
47 | printf ( "Socket TCP_LINGER2 = %d\n" , iSocketOption); |
49 | getsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, ( char *)&iSocketOption, &iSocketOptionLen); |
50 | printf ( "Socket SO_REUSEADDR = %d\n" , iSocketOption); |
52 | getsockopt(socketHandle, SOL_SOCKET, SO_ERROR, ( char *)&iSocketOption, &iSocketOptionLen); |
53 | printf ( "Socket SO_ERROR = %d\n" , iSocketOption); |
55 | getsockopt(socketHandle, SOL_SOCKET, SO_BROADCAST, ( char *)&iSocketOption, &iSocketOptionLen); |
56 | printf ( "Socket SO_BROADCAST = %d\n" , iSocketOption); |
58 | getsockopt(socketHandle, SOL_SOCKET, SO_KEEPALIVE, ( char *)&iSocketOption, &iSocketOptionLen); |
59 | printf ( "Socket SO_KEEPALIVE = %d\n" , iSocketOption); |
61 | getsockopt(socketHandle, SOL_SOCKET, SO_SNDBUF, ( char *)&iSocketOption, &iSocketOptionLen); |
62 | printf ( "Socket SO_SNDBUF = %d\n" , iSocketOption); |
64 | getsockopt(socketHandle, SOL_SOCKET, SO_RCVBUF, ( char *)&iSocketOption, &iSocketOptionLen); |
65 | printf ( "Socket SO_RCVBUF = %d\n" , iSocketOption); |
67 | getsockopt(socketHandle, SOL_SOCKET, SO_LINGER, ( char *)&SocketOptionLinger, &iSocketOptionLingerLen); |
68 | printf ( "Socket SO_LINGER = %d time = %d\n" , SocketOptionLinger.l_onoff, SocketOptionLinger.l_linger); |
70 | getsockopt(socketHandle, SOL_SOCKET, SO_RCVLOWAT, ( char *)&iSocketOption, &iSocketOptionLen); |
71 | printf ( "Socket SO_RCVLOWAT = %d\n" , iSocketOption); |
Compile:
gcc -o printSocketOptions printSocketOptions.c
getsockopt man page: get a particular socket option for the specified socket.
Set socket options:
- Socket "keep-alive":
3 | if (setsockopt(socketHandle, SOL_SOCKET, SO_KEEPALIVE, ( const char *) &iOption, sizeof ( int )) == SOCKET_ERROR) |
5 | cerr << "Set keepalive: Keepalive option failed" << endl; |
Set socket client options:
- Socket re-use:
3 | if (setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, ( const char *) &iOption, sizeof ( int )) == SOCKET_ERROR) |
5 | cerr << "Set reuse address: Client set reuse address option failed" << endl; |
When a socket connection is closed with a call to close(), shutdown() or exit(), both the client and server will send a FIN (final) packet and will then send an acknowledgment (ACK) that they received the packet.
The side which initiates the closure will be in a TIME_WAIT state until the process has been completed.
This time out period is generally 2-4 minutes in duration. It is hoped that all packets are received in a timely manner and the entire time out duration is not required. When an application is abnormally terminated, the TIME_WAIT period is entered for the full duration.
Setting the SO_REUSEADDR option explicitly allows a process to bind a port in the TIME_WAIT state. This is to avoid the error "bind: Address Already in Use". One caviat is that the process can not be to the same address and port as the previous connection. If it is, the SO_REUSEADDR option will not help and the duration of the TIME_WAIT will be in effect.
For more info see How to avoid the "Address Already in Use" error.
Solution: Enable socket linger:
5 | if (setsockopt(socketHandle, SOL_SOCKET, SO_LINGER, ( const char *) &Option, sizeof (linger)) == -1) |
7 | cerr << "Set SO_LINGER option failed" << endl; |
This allows the socket to die quickly and allow the address to be reused again.
Warning: This linger configuration specified may/will result in data loss upon socket termination, thus it would not have the robustness required for a banking transaction but would be ok for a recreational app.
- Broadcast:
3 | if (setsockopt(socketHandle, SOL_SOCKET, SO_BROADCAST, ( const char *) &iOption, sizeof ( int )) == SOCKET_ERROR) |
5 | cerr << "Set reuse address: Client set reuse address option failed" << endl; |
Struct: remoteSocketInfo.sin_addr.s_addr = htonl(INADDR_BROADCAST);
setsockopt man page: set a particular socket option for the specified socket.
Test Socket Availability:
Function to test if a socket or set of sockets has data and can be read (Test so you don't get blocked on a read) or written.
01 | #include <sys/select.h> |
07 | bool isReadyToRead( int _socketHandle, const long &_lWaitTimeMicroseconds) |
09 | int iSelectReturn = 0; |
12 | fd_set readSetOfSockets; |
16 | timeToWait.tv_sec = 0; |
17 | timeToWait.tv_usec = _lWaitTimeMicroseconds; |
19 | FD_ZERO(&readSetOfSockets); |
20 | FD_SET(_socketHandle, &readSetOfSockets); |
22 | if (_socketHandle > fd_max) |
24 | fd_max = _socketHandle; |
27 | iSelectReturn = select(fd_max + 1, &readSetOfSockets, (fd_set*) 0, (fd_set*) 0, &timeToWait); |
30 | if ( iSelectReturn == 0 ) |
34 | else if ( iSelectReturn < 0 ) |
36 | cerr << "*** Failed with error " << errno << " ***" << endl; |
44 | if ( FD_ISSET(_socketHandle, &readSetOfSockets) ) |
Arguments to
select():
- int fd_max: Highest socket descriptor number. When opening a socket with the call socket(), the function returns a socket descriptor number.
The call to select() will loop through all of the socket descriptors from zero up to fd_max to perform the "test".
- fd_set *readSetOfSockets: This is a pointer to the variable holding the set of bits representing the set of sockets to test for readability. (Read will not block)
The default number of bytes detected for the socket to be considered ready to read is 1. To change this default use (in this example 8 bytes):
int nBytes = 8;
setsockopt(socketHandle, SOL_SOCKET, SO_RCVLOWAT,(const char *) &nBytes, sizeof(int))
- fd_set *writeSetOfSockets: This is a pointer to the variable holding the set of bits representing the set of sockets to test for writeability. (Write will not block)
- fd_set *exceptfds: This is a pointer to the variable holding the set of bits representing the set of sockets to test for exceptions.
- struct timeval *timeout: This structure holds the upper bound on the amount of time elapsed before select returns. It may be zero, causing select to return immediately. If timeout is NULL (no timeout), select can block indefinitely.
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
Note: Any of the tests (read/write/exceptions) can be set to NULL to ignore that test.
Also see the select man page
Port Numbers:
The use of port numbers below 1024 are limited to the root process.
For a list of port numbers with established uses see the file /etc/services.
Posts are 16 bit identifiers. Many are reserved and managed by the Internet Assigned Numbers Authority (IANA).
See RFC 1700.
Host and Network Byte Order:
Note that when transferring data between different platforms or with Java, the byte order endianess will have to be considered.
The network (the neutral medium) byte order is Big Endian and the byte order to which data is usually marshalled.
Host processor byte order:
Host Processor | Endianness |
Intel x86 processor family | Little endian |
Power PC processor family | Big endian |
SUN SPARC | Big endian |
Mips | Big endian (IRIX) |
Mips | Little endian (NT) |
Note that it is the processor architecture which determines the endianness and NOT the OS.
The exception is for processors which support both big and little endian byte ordering, such as the MIPS processor.
Also note: Java processes and stores data in big endian byte ordering on any platform.
Character data is not subject to this problem as each character is one byte in length but integer is.
Integer data can be converted from/to host or network byte order with the following routines:
Function | Description |
ntohl() | Network to host byte order conversion for long integer data (uint32_t) |
ntohs() | Network to host byte order conversion for short integer data (uint16_t) |
htonl() | Host to network byte order conversion for long integer data (uint32_t) |
htons() | Host to network byte order conversion for short integer data (uint16_t) |
Requires
#include <arpa/inet.h>
The routines are aware of the processor they are running on and thus either perform a conversion or not, depending if it is required.
Man pages:
Note that one uses these calls for portable code on any platform.
The port of the library to that platform determines whether a byte swap will occur and not your source code.
Include file/code snipit to determine the processor endianess:
File:
is_bigendian.h
1 | #ifndef __IS_BIGENDIAN__ |
2 | #define __IS_BIGENDIAN__ |
6 | #define is_bigendian() ( (*(char*)&bsti) == 0 ) |
8 | #endif // __IS_BIGENDIAN__ |
Code snipit to swap bytes "in place" and convert endian:
02 | #include "is_bigendian.h" |
10 | void swapbytes( void *_object, size_t _size) |
12 | unsigned char *start, *end; |
16 | for ( start = (unsigned char *)_object, end = start + _size - 1; start < end; ++start, --end ) |
18 | unsigned char swap = *start; |
Swaping bit field structures:
02 | #include "is_bigendian.h" |
04 | void swapbytes( void *_object, size_t _size); |
44 | struct ExampleA exampleA; |
58 | memcpy (&tmpStore, &exampleA, sizeof (ExampleA)); |
60 | swapbytes(( void *)&tmpStore, sizeof (ExampleA)); |
For complete coverage on big and little endian byte order and byte order conversion see the YoLinux Endian Byte Order tutorial.
Socket Tips and Best Practices:
- Re-connect: If a socket client attempts to connect to a socket server and fails, one should attempt to re-attach after a given waiting period, a fixed number of times.
10 | while ( connect(socketHandle, ( struct sockaddr *)&remoteSocketInfo, sizeof (sockaddr_in)) < 0 ) |
13 | if ( iTry > mRetrymax ) |
15 | cerr << "Failed to connect to server. Exceeded maximum allowable attempts: " << mRetrymax << endl; |
- Check return codes: Just because the socket was opened doesn't mean it will stay open. Dropped connections do happen. Check all read/write socket API function return codes:
- return code > 0: Data received/sent
- return code == 0: Socket closed
- return code == -1: Check system errno and interpret (or call "perror()")
For even more robust code for a socket client, close, then open a new socket connection for return codes 0 or -1.
- Read entire message: (Variable and fixed length messages) When sending messages using a socket, be aware that the other side of connection must know how much to read. This means that you must:
- Send fixed message sizes of a known number of bytes where all messages sent are of the same size
or
- Send a message with a header of a known size where the message size is sent in the header. Thus read the fixed size header and determine the size of the whole message then read the remaining bytes.
Note that with a UDP client/server communications, the socket read will read the whole frame sent.
This is not necessarily true for a TCP stream. The function used to read the stream will return the number of bytes read.
If the required number of bytes have not been read, repeat the read for the remaining bytes until the whole message has been read.
In this example, our message header is a single integer used to store
the message size in the first 32 bits of the message.
03 | #include <sys/socket.h> |
17 | int remainingMessageSize = 0; |
19 | int messageHeaderSize = sizeof ( int ); |
20 | unsigned char *headerBuf= new unsigned char [messageHeaderSize ]; |
29 | rc = recv(socketConnection, headerBuf, messageHeaderSize , 0); |
32 | cerr << "Socket closed" << endl; |
34 | cerr << "Socket error" << endl; |
35 | else if ( rc != messageHeaderSize ) |
36 | cerr << "Problem reading header" << endl; |
41 | int tmp = htonl((uint32_t)headerBuf); |
42 | memcpy (( void *)&messageSize,( void *)&tmp, messageHeaderSize ); |
45 | unsigned char *messageTotalBuf = new unsigned char [messageTotalSize ]; |
48 | memcpy (messageTotalBuf, &headerBuf, messageHeaderSize ); |
51 | remainingMessageSize = messageTotalSize - messageHeaderSize; |
54 | messageTotalBuf += messageHeaderSize; |
58 | rc = recv(socketConnection, messageTotalBuf, remainingMessageSize, 0); |
62 | cerr << "Socket closed" << endl; |
67 | cerr << "Socket error" << endl; |
69 | close(socketConnection); |
72 | else if ( rc != remainingMessageSize) |
75 | remainingMessageSize = remainingMessageSize - rc; |
79 | messageTotalBuf += rc; |
This example applies to either client or server TCP sockets.
Another method of ensuring a complete read of a fixed size message is to use the MSG_WAITALL flag:
4 | int flags = MSG_WAITALL; |
5 | int rc = recv(socketConnection, buffer, length, flags); |
On "SOCK_STREAM" (TCP) sockets, this flag requests that the recv() function block until the full amount of data specified (length) can be returned.
The function may return the smaller amount of data if the socket is a message-based socket, if a signal is caught or if the connection is terminated.
- UDP message order: TCP will guarentee that the order of the message delivery is maintained.
UDP will not guarentee the message delivery order and thus you will have to maintain a counter in the message if the order is important.
- Signals: The socket layer can issue signals, which if not handled, can terminate your application.
When a socket is terminated at one end, the other may receive a SIGPIPE signal.
Program received signal SIGPIPE, Broken pipe.
0x000000395d00de45 in send () from /lib64/libpthread.so.0
(gdb) where
#0 0x000000395d00de45 in send () from /lib64/libpthread.so.0
#1 0x00000000004969c5 in bla bla bla
...
...
Note GDB will report a received signal even if it's being ignored by the application.
In this example, we tried to send() over a broken socket link.
Set up a signal handler at the beginning of your program to handle SIGPIPE:
05 | void handleSigpipe( int signum) |
07 | cout << "SIGPIPE ignored" << endl; |
15 | signal (SIGPIPE, handleSigpipe); |
Also see:
Win32 and cross platform considerations:
The beauty of the BSD API is that it is nativly supported by Microsoft Visual C++ but there are some slight differences which can be wrapped in an "ifdef".
01 | #ifdef WIN32 // Windows |
09 | #include <sys/socket.h> |
18 | memset (&oWSAData, 0, sizeof (oWSAData)); |
31 | if ((iWSAError = WSAStartup(MAKEWORD(2, 2), &oWSAData)) != 0) |
33 | cerr << "Creating the WSAStartup failed with error " << iWSAError << endl; |
44 | closesocket(socketHandle); |
51 | cerr << "Failed with error " |
53 | << WSAGetLastError() << endl; |
Note that the declaration and error handling are different and platform dependent. The close() function call was made a little more specific for the Win32 platform.
Microsoft Visual C++ settings (VC5.0):
"Configuration Properties" --> "C/C++" --> "Preprocessor"
Add:
UTILSOCKETS_USE_IPC=0
Error codes: The error codes returned by the MS/Windows BSD API socket calls use different defined macro representations and may have to be handled differently.
Config files: MS/Windows has an equivalent of /etc/hosts which can be found in %SYSTEMROOT%\SYSTEM32\DIRVERS\ETC\HOSTS where the format is identical to the Unix/Linux file format.
Socket BSD API man pages:
Sockets API:
- socket: establish socket interface
- gethostname: obtain hostname of system
- gethostbyname: returns a structure of type hostent for the given host name
- bind: bind a name to a socket
- listen: listen for connections on a socket
- accept: accept a connection on a socket
- connect: initiate a connection on a socket
- setsockopt: set a particular socket option for the specified socket.
- close: close a file descriptor
- shutdown: shut down part of a full-duplex connection
Interrogate a socket:
- select: synchronous I/O multiplexing
- FD_ZERO(), FD_CLR(), FD_SET(), FD_ISSET(): Set socket bit masks
- poll: check on the state of a socket in a set of sockets. The set can be tested to see if any socket can be written to, read from or if an error occurred.
- getsockopt: retrieve the current value of a particular socket option for the specified socket.
Read/Write:
Convert:
Other supporting system calls:
- exit: Terminate process
- perror: Output explanation of an error code
- protocols: Network Protocols (see /etc/protocols)
Links:

Books:
 |
"UNIX Network Programming, Volume 1: Sockets Networking API" Third Edition
by W. Richard Stevens, Bill Fenner, Andrew M. Rudoff, Richard W. Stevens
ISBN # 0131411551, Addison-Wesley Pub Co; 3 edition (October 22, 2003)
This book covers POSIX, IPv6, network APIs, sockets
(elementary, advanced, routed, and raw), multicast, UDP, TCP, Threads,
Streams, ioctl. In depth coverage of topics. I recommend this book for any sockets programming task.
|
|
 |
"UNIX Network Programming, Volume 1: Networking APIs - Sockets and XTI" Second Edition
by W. Richard Stevens
ISBN # 013490012X, Prentice Hall PTR
This book covers network APIs, sockets + XTI,
multicast, UDP, TCP, ICMP, raw sockets, SNMP, MBONE. In depth coverage
of topics.
|
|
 |
"UNIX Network Programming Volume 2: Interprocess Communications"
by W. Richard Stevens
ISBN # 0130810819, Prentice Hall PTR
This book covers semaphores, threads, record locking, memory mapped I/O,
message queues, RPC's, etc.
|
|
 |
TCP/IP Sockets in C, Second Edition: Practical Guide for Programmers
by Michael J. Donahoo, Kenneth L. Calvert
ISBN #0123745403
IPv4 and IPv6
|
|
 |
"Advanced Linux Programming"
by Mark Mitchell, Jeffrey Oldham, Alex Samuel, Jeffery Oldham
ISBN # 0735710430, New Riders
Good book for programmers who already know how to program and just need
to know the Linux specifics. Covers a variety of Linux tools, libraries,
API's and techniques. If you don't know how to program, start with a
book on C.
|
|
 |
"Advanced UNIX Programming" Second Edition
by Marc J. Rochkind
ISBN # 0131411543, Addison-Wesley Professional Computing Series
|
|
 |
"Advanced Programming in the UNIX Environment" First Edition
by W. Richard Stevens
ISBN # 0201563177, Addison-Wesley Professional Computing Series
It is the C programmers guide to programming on the UNIX platform.
This book is a must for any serious UNIX/Linux programmer. It covers all
of the essential UNIX/Linux API's and techniques.
This book starts where the basic C programming book leaves off.
Great example code.
This book travels with me to every job I go to.
|
|
 |
"Advanced Unix Programming"
by Warren W. Gay
ISBN # 067231990X, Sams White Book Series
This book covers all topics in general: files,
directories, date/time, libraries, pipes, IPC, semaphores, shared
memory, forked processes and I/O scheduling. The coverage is not as in
depth as the previous two books (Stevens Vol 1 and 2)
|
|
 |
"Linux Programming Bible"
by John Goerzen
ISBN # 0764546570, Hungry Minds, Inc
This covers the next step after "C" programming 101.
|
|
 |
C++ How to Program
by Harvey M. Deitel, Paul J. Deitel
ISBN #0131857576, Prentice Hall
Fifth edition. The first edition of this book (and Professor Sheely at UTA) taught me to program C++. It is complete and covers all the nuances of the C++ language.
It also has good code examples. Good for both learning and reference.
|
|