The code is dense, but totally accessible to us.
- Provided a server is prepared to listen to a specified port on any of its IP addresses, the following function returns a properly configured server socket:
static const int kServerSocketFailure = -1; // sentinel for no valid socket
static const int kReuseAddresses = 1; // 1 means true here
static const int kDefaultBacklog = 128; // allow 128 clients to queue up before they are "accept"ed, drop/ignore 129th
int createServerSocket(unsigned short port) {
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
if (serverSocket < 0) return kServerSocketFailure;
if (setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR,
&kReuseAddresses, sizeof(int)) < 0) {
close(serverSocket);
return kServerSocketFailure;
} // setsockopt used here so port becomes available even is server crashes and reboots
struct sockaddr_in serverAddress; // IPv4-style socket address
memset(&serverAddress, 0, sizeof(serverAddress));
serverAddress.sin_family = AF_INET; // sin_family field used to self-identify sockaddr type
serverAddress.sin_addr.s_addr = htonl(INADDR_ANY);
serverAddress.sin_port = htons(port);
if (bind(serverSocket, (struct sockaddr *) &serverAddress, sizeof(struct sockaddr_in)) == 0 &&
listen(serverSocket, kDefaultBacklog) == 0) return serverSocket;
close(serverSocket);
return kServerSocketFailure;
}
- Note that createServerSocket returns a socket we listen to for incoming connections.
- It's categorized as a descriptor, so it needs to be closed when we're done with it.
- It also gets cloned across fork boundaries as any descriptor would.
- Server sockets, however, are not compatible with read and write system calls. The only "read"-oriented system call it's compatible with is accept.