To establish TCP connections the server host calls socket() to create a listening socket then specifies the IP address and TCP port on which the server will receive connection requests with a call to bind(). Calling listen() puts the server into listening mode which then blocks on the accept() waiting for incoming connections.
The client connects to the server by calling socket() then connect() with a socket address that includes the IP address and TCP port specifying used for the bind() call on the server. On the server the accept() function returns with a connection socket descriptor when the client’s connection request is received.
After connecting the server blocks on a call to read() waiting for a client request. The client calls write() to send a request then blocks on a call to read() waiting for the server’s response. When the server is done processing the request, it sends back a response to the client. The exchange of requests and responses repeats until the client is done, at which time it closes the connection. The server detects this event when read() returns 0. The server responds by closing its end of the connection then returning to get another connection.
In most servers connections are accepted in one thread and a new thread or process is created to handle each connection. To keep things simple the example here describes an iterative server where each request is handled one at a time.