Sockets are a method of IPC that allow data to be exchanged between applications, either on the same host (computer) or on different hosts connected by a network. The first widespread implementation of the sockets API appeared with 4.2BSD in 1983, and this API has been ported to virtually every UNIX implementation, as well as most other operating systems.
The sockets API is formally specified in POSIX.1g, which was ratified in 2000 after spending about a decade as a draft standard. This standard has been superseded by SUSv3.
This chapter and the following chapters describe the use of sockets, as follows:
This chapter provides a general introduction to the sockets API. The following chapters assume an understanding of the general concepts presented here. We don’t present any example code in this chapter. Code examples in the UNIX and Internet domains are presented in the following chapters.
Chapter 57 describes UNIX domain sockets, which allow communication between applications on the same host system.
Chapter 58 introduces various computer networking concepts and describes key features of the TCP/IP networking protocols. It provides background needed for the next chapters.
Chapter 59 describes Internet domain sockets, which allow applications on different hosts to communicate via a TCP/IP network.
Chapter 60 discusses the design of servers that use sockets.
Chapter 61 covers a range of advanced topics, including additional features for socket I/O, a more detailed look at the TCP protocol, and the use of socket options to retrieve and modify various attributes of sockets.
These chapters merely aim to give the reader a good grounding in the use of sockets. Sockets programming, especially for network communication, is an enormous topic in its own right, and forms the subject of entire books. Sources of further information are listed in Further Information.
In a typical client-server scenario, applications communicate using sockets as follows:
Each application creates a socket. A socket is the “apparatus” that allows communication, and both applications require one.
The server binds its socket to a well-known address (name) so that clients can locate it.
A socket is created using the socket() system call, which returns a file descriptor used to refer to the socket in subsequent system calls:
fd = socket(domain, type, protocol);
We describe socket domains and types in the following paragraphs. For all applications described in this book, protocol is always specified as 0.
Sockets exist in a communication domain, which determines:
the method of identifying a socket (i.e., the format of a socket “address”); and
the range of communication (i.e., either between applications on the same host or between applications on different hosts connected via a network).
Modern operating systems support at least the following domains:
The UNIX (AF_UNIX
) domain allows communication between applications on the same host. (POSIX.1g used the name AF_LOCAL
as a synonym for AF_UNIX
, but this name is not used in SUSv3.)
The IPv4 (AF_INET
) domain allows communication between applications running on hosts connected via an Internet Protocol version 4 (IPv4) network.
The IPv6 (AF_INET6
) domain allows communication between applications running on hosts connected via an Internet Protocol version 6 (IPv6) network. Although IPv6 is designed as the successor to IPv4, the latter protocol is currently still the most widely used.
Table 56-1 summarizes the characteristics of these socket domains.
In some code, we may see constants with names such as PF_UNIX
instead of AF_UNIX
. In this context, AF
stands for “address family” and PF
stands for “protocol family.” Initially, it was conceived that a single protocol family might support multiple address families. In practice, no protocol family supporting multiple address families has ever been defined, and all existing implementations define the PF_
constants to be synonymous with the corresponding AF_
constants. (SUSv3 specifies the AF_
constants, but not the PF_
constants.) In this book, we always use the AF_
constants. Further information about the history of these constants can be found in Universality of I/O of [Stevens et al., 2004].
Table 56-1. Socket domains
Domain | Communication performed | Communication between applications | Address format | Address structure |
---|---|---|---|---|
| within kernel | on same host | pathname | sockaddr_un |
| via IPv4 | on hosts connected via an IPv4 network | 32-bit IPv4 address + 16-bit port number | sockaddr_in |
| via IPv6 | on hosts connected via an IPv6 network | 128-bit IPv6 address + 16-bit port number | sockaddr_in6 |
Every sockets implementation provides at least two types of sockets: stream and datagram. These socket types are supported in both the UNIX and the Internet domains. Table 56-2 summarizes the properties of these socket types.
Stream sockets (SOCK_STREAM
) provide a reliable, bidirectional, byte-stream communication channel. By the terms in this description, we mean the following:
Reliable means that we are guaranteed that either the transmitted data will arrive intact at the receiving application, exactly as it was transmitted by the sender (assuming that neither the network link nor the receiver crashes), or that we’ll receive notification of a probable failure in transmission.
Bidirectional means that data may be transmitted in either direction between two sockets.
Byte-stream means that, as with pipes, there is no concept of message boundaries (refer to Overview).
A stream socket is similar to using a pair of pipes to allow bidirectional communication between two applications, with the difference that (Internet domain) sockets permit communication over a network.
Stream sockets operate in connected pairs. For this reason, stream sockets are described as connection-oriented. The term peer socket refers to the socket at the other end of a connection; peer address denotes the address of that socket; and peer application denotes the application utilizing the peer socket. Sometimes, the term remote (or foreign) is used synonymously with peer. Analogously, sometimes the term local is used to refer to the application, socket, or address for this end of the connection. A stream socket can be connected to only one peer.
Datagram sockets (SOCK_DGRAM
) allow data to be exchanged in the form of messages called datagrams. With datagram sockets, message boundaries are preserved, but data transmission is not reliable. Messages may arrive out of order, be duplicated, or not arrive at all.
Datagram sockets are an example of the more generic concept of a connectionless socket. Unlike a stream socket, a datagram socket doesn’t need to be connected to another socket in order to be used. (In Using connect() with Datagram Sockets, we’ll see that datagram sockets may be connected with one another, but this has somewhat different semantics from connected stream sockets.)
In the Internet domain, datagram sockets employ the User Datagram Protocol (UDP), and stream sockets (usually) employ the Transmission Control Protocol (TCP). Instead of using the terms Internet domain datagram socket and Internet domain stream socket, we’ll often just use the terms UDP socket and TCP socket, respectively.
The key socket system calls are the following:
The bind() system call binds a socket to an address. Usually, a server employs this call to bind its socket to a well-known address so that clients can locate the socket.
The listen() system call allows a stream socket to accept incoming connections from other sockets.
The accept() system call accepts a connection from a peer application on a listening stream socket, and optionally returns the address of the peer socket.
The connect() system call establishes a connection with another socket.
On most Linux architectures (the exceptions include Alpha and IA-64), all of the sockets system calls are actually implemented as library functions multiplexed through a single system call, socketcall(). (This is an artifact of the original development of the Linux sockets implementation as a separate project.) Nevertheless, we refer to all of these functions as system calls in this book, since this is what they were in the original BSD implementation, as well as in many other contemporary UNIX implementations.
Socket I/O can be performed using the conventional read() and write() system calls, or using a range of socket-specific system calls (e.g., send(), recv(), sendto(), and recvfrom()). By default, these system calls block if the I/O operation can’t be completed immediately. Nonblocking I/O is also possible, by using the fcntl() F_SETFL
operation (Open File Status Flags) to enable the O_NONBLOCK
open file status flag.
On Linux, we can call ioctl(fd, FIONREAD, &cnt) to obtain the number of unread bytes available on the stream socket referred to by the file descriptor fd. For a datagram socket, this operation returns the number of bytes in the next unread datagram (which may be zero if the next datagram is of zero length), or zero if there are no pending datagrams. This feature is not specified in SUSv3.