§2.7.2Socket Programming with TCP

Application layer Kurose & Ross pp. 159–164 · ~17 min read

  • welcoming socket
  • connection socket
  • connection-oriented service
  • handshaking
  • reliable data transfer

Where you are

  • Application layer you are here
  • Transport layer
  • Network layer
  • Link layer
  • Physical layer

The same application over TCP (Transmission Control Protocol) gains a connection — which means a handshake before anything moves, a second socket at the server, and eight packets where UDP (User Datagram Protocol) needed two.

Words you will meet

  • SOCK_STREAM — the argument that makes a socket a TCP socket.
  • Welcoming socket — the server socket whose only job is to listen for knocks.
  • Connection socket — a fresh socket the server makes for one particular client.
  • connect() — the client call that triggers the three-way handshake.
  • accept() — the server call that returns a new connection socket.
  • listen() — puts the welcoming socket into listening state.

Why this matters

This is the last page of the chapter’s content, and it closes a loop that opened in section 2.1.4. That section listed what TCP offers and what it costs. This one shows both, in code you can run and in packets you can count.

The welcoming socket is also worth the attention the book gives it. The book says directly that students meeting TCP sockets for the first time confuse it with the connection socket. It is the one idea on this page a reader can carry away wrong without noticing.

What a connection changes

Unlike UDP (User Datagram Protocol) , TCP (Transmission Control Protocol) is a connection-oriented protocol. Before client and server can send anything to each other, they first handshake and establish a TCP connection.

One end of that connection is attached to the client socket and the other to a server socket. When it is created, the connection is associated with both socket addresses: the client’s IP (Internet Protocol) address and port, and the server’s IP address and port.

In plain words — this is why send() takes no address

With the connection established, when one side wants to send data it just drops the data into the connection through its socket.

That is different from UDP, where the sending process had to attach a destination address to the packet before dropping it into the socket. Here the connection already knows where both ends are.

One fact, and it explains send() against sendto(), recv() against recvfrom(), and the existence of connect() in the first place.

Knocking on the welcoming door

The client initiates contact, so the server must be ready. That means two things.

First, as with UDP, the server must be running as a process before the client attempts contact. Second, the server program must have a special socket that welcomes initial contact from a client process running on an arbitrary host.

Using the house-and-door analogy from section 2.1.2, the book calls the client’s first contact “knocking on the welcoming door”.

When the client creates its TCP socket it specifies the address of that welcoming socket — the server’s IP address and port number. It then initiates the three-way handshake.

The handshake is invisible to both programs

The three-way handshake, which takes place within the transport layer, is completely invisible to the client and server programs.

Neither program contains a line that constructs a SYN (synchronize) , or waits for one, or acknowledges one. Hold on to that sentence — the capture at the end of this page shows those three packets existing anyway, and what they cost.

When the server hears the knocking, it creates a new connection socket dedicated to that particular client. In the code below the welcoming door is serverSocket; the newly created socket dedicated to this client is connectionSocket.

Client processServer processClient socketWelcomingsocketConnectionsocketThree-way handshakearrives at the welcoming socketbytesEvery byte of data crosses here, never through the welcoming socket.Figure 2.28 — the TCPServer process has two sockets, and only one of them ever carries data.
One welcoming socket, one connection socket per client
Before anyone arrivesstep 1 of 4
Server processserverSocketthe welcoming socketlistening on port 12000blocked in accept(), waiting for a knock

serverSocket is created, bound to port 12000, and put into listening state. It is the welcoming socket: the initial point of contact for every client that will ever connect. It carries no application data, now or ever.

Two clients arrive in turn. Watch which socket survives and which one does not.

Read all steps as text
  1. Before anyone arrivesserverSocket is created, bound to port 12000, and put into listening state. It is the welcoming socket: the initial point of contact for every client that will ever connect. It carries no application data, now or ever.
  2. Client A knocksThe client’s connect() triggers the three-way handshake. When the server “hears” the knocking, accept() returns a NEW socket — connectionSocket — dedicated to this one client. serverSocket is untouched.
  3. Client A is served, and its socket is closedThe server reads the sentence, capitalises it, sends it back, and calls connectionSocket.close(). That ends this client’s conversation and nothing else. The loop returns to accept().
  4. Client B knocksBecause serverSocket is still open, another client can knock on the door and send the server a sentence to modify. accept() produces a second connection socket, entirely separate from the first.

In plain words

From the application’s perspective, the client’s socket and the server’s connection socket are directly connected by a pipe. The client can send arbitrary bytes into its socket, and TCP guarantees the server will receive every byte, in the order sent.

And just as people go in and out through the same door, the pipe runs both ways. The client both sends into and receives from its socket, and the server does the same with its connection socket.

TCPClient.py

The whole client

from socket import *
serverName = 'servername'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)
clientSocket.connect((serverName, serverPort))
sentence = input('Input lowercase sentence:')
clientSocket.send(sentence.encode())
modifiedSentence = clientSocket.recv(1024)
print('From Server: ', modifiedSentence.decode())
clientSocket.close()

Only a few lines differ significantly from the UDP version, and each difference is the connection showing through.

LineWhat changed, and why
socket(AF_INET, SOCK_STREAM)SOCK_STREAM instead of SOCK_DGRAM — a TCP socket. As before, the client’s own port number is not specified; the operating system chooses it.
clientSocket.connect((serverName, serverPort))New. The parameter is the address of the server side of the connection. After this line executes, the three-way handshake has been performed and a TCP connection exists.
clientSocket.send(sentence.encode())Sends the sentence through the socket and into the connection. The program does not create a packet or attach a destination address, as the UDP version had to — it simply drops the bytes in.
modifiedSentence = clientSocket.recv(1024)Characters arriving from the server accumulate here.
clientSocket.close()Closes the socket, and with it the TCP connection. The book notes this causes TCP in the client to send a TCP message to TCP in the server — packet 7 in the capture below.

A small inconsistency in the book

The code listing on book page 161 reads clientSocket.recv(1024). The line-by-line walkthrough two pages later quotes the same line as clientSocket.recv(2048).

Either works — the argument is only the maximum number of bytes to read at once, and this sentence is far shorter than both. The listing is the authoritative version, so this site uses 1024. Flagged because a reader comparing the two pages would otherwise think they had misread one of them.

TCPServer.py

The whole server

from socket import *
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(1)
print('The server is ready to receive')
while True:
    connectionSocket, addr = serverSocket.accept()
    sentence = connectionSocket.recv(1024).decode()
    capitalizedSentence = sentence.upper()
    connectionSocket.send(capitalizedSentence.encode())
    connectionSocket.close()
LineWhat it does
socket(AF_INET, SOCK_STREAM)Creates the socket that will become the welcoming socket .
serverSocket.bind(('', serverPort))Associates port 12000 with it, exactly as the UDP server did.
serverSocket.listen(1)New. Has the server listen for TCP connection requests. The parameter is the maximum number of queued connections — at least 1.
connectionSocket, addr = serverSocket.accept()When a client knocks on this door, accept() creates a new socket in the server, dedicated to that particular client. The client and server then complete the handshaking.
recv(1024).decode() then .upper()Read the sentence and capitalise it — the same work as before.
connectionSocket.send(...)The reply goes out over the connection socket, not the welcoming one.
connectionSocket.close()Closes this client’s socket. But serverSocket remains open, so another client can now knock on the door and send a sentence to modify.

The confusion the book warns about

Students who are encountering TCP sockets for the first time sometimes confuse the welcoming socket … and each newly created server-side connection socket.

The test is simple. Which socket do the bytes cross? Always the connection socket. The welcoming socket produces connection sockets and does nothing else, for the entire life of the server.

Server(running on serverIP)ClientCreate socket, port=x,for incoming request:serverSocket = socket()Wait for incomingconnection request:connectionSocket = serverSocket.accept()Create socket, connectto serverIP, port=x:clientSocket = socket()TCPconnection setupSend request usingclientSocketRead request fromconnectionSocketWrite reply toconnectionSocketRead reply fromclientSocketCloseconnectionSocketwhile TrueCloseclientSocket

The two versions, side by side

UDP sockets and TCP sockets, line by line
UDP§2.7.1TCPthis section
Socket type argument
Before any data moves
Sending a message
Receiving a message
Sockets at the server
What the server closes
What arrives
Packets for one exchange

Cells marked ⓘ have a reason behind them — click to read it.

The same application, written twice. Every difference traces back to one fact. Click any ⓘ.

What the connection actually costs

Section 2.7.1 captured the UDP version of this application: two packets. Here is the TCP version of the same exchange.

The same application over TCP — eight packets instead of two
No.TimeSourceDestinationProtocolLengthInfo
10.000000192.168.1.24198.51.100.7TCP5451874 → 12000 [SYN] Seq=0 Win=64240 Len=0
20.090400198.51.100.7192.168.1.24TCP5412000 → 51874 [SYN, ACK] Seq=0 Ack=1 Win=65535 Len=0
30.180800192.168.1.24198.51.100.7TCP5451874 → 12000 [ACK] Seq=1 Ack=1 Win=64240 Len=0
40.181100192.168.1.24198.51.100.7TCP7251874 → 12000 [PSH, ACK] Seq=1 Ack=1 Len=18
50.271800198.51.100.7192.168.1.24TCP7212000 → 51874 [PSH, ACK] Seq=1 Ack=19 Len=18
60.271900198.51.100.7192.168.1.24TCP5412000 → 51874 [FIN, ACK] Seq=19 Ack=19 Len=0
70.362300192.168.1.24198.51.100.7TCP5451874 → 12000 [FIN, ACK] Seq=19 Ack=20 Len=0
80.452700198.51.100.7192.168.1.24TCP5412000 → 51874 [ACK] Seq=20 Ack=20 Len=0

Packet 1 Packet 1 of 8. clientSocket.connect() produced this, and the client program has not yet sent a single byte of the sentence. The UDP version was already finished sending by now.

Protocol tree — click a field

The actual bytes

0000 aa bb cc 00 00 01 aa bb cc 00 00 11 08 00 45 00 ..............E.
0010 00 28 1a 01 40 00 40 06 34 d4 c0 a8 01 18 c6 33 .(..@.@.4......3
0020 64 07 ca a2 2e e0 3f 8a 12 c0 00 00 00 00 50 02 d.....?.......P.
0030 fa f0 7d 29 00 00 ..})..

TCPClient.py and TCPServer.py exchanging one sentence. Six of these eight packets carry no application data at all: three open the connection and three more close it.

Six of these eight packets carry no data

Three open the connection. Two carry the sentence and its reply. Three close it again.

Now look back at the box above, where the book said the handshake is completely invisible to the client and server programs. That is true of the programs and false of the network. Packets 1, 2 and 3 correspond to no line of code in either file. A full round-trip time passes before packet 4 carries the first byte of the sentence.

This is the cost section 2.2.2 measured for HTTP (HyperText Transfer Protocol) , seen from the other side. It is also why section 2.1.4 said developers of Internet telephony applications prefer UDP: for an exchange this short, the connection costs more packets than the conversation.

Running them yourself

Run the two programs in two separate hosts, then compare them with the UDP pair from section 2.7.1. Reading them side by side is worth more than reading either alone.

Then modify them. The book suggests trying the socket programming assignments at the end of this chapter and the next ones. They build a web server, a UDP pinger, a mail client and a web proxy from exactly these foundations.

Where this differs from the UDP version: if the server is not running, a TCP client fails immediately and loudly, because connect() gets no answer to its SYN. The UDP client in the same situation blocks forever with no indication that anything is wrong. That is not a quality-of-implementation difference — it is the connection existing, and being the thing that failed.

Check yourself

Check yourself

0 of 7 answered
  1. 1.A TCP server ends up with two sockets. What is each one for?

  2. 2.`clientSocket.send(sentence.encode())` passes no address. How does the data know where to go?

  3. 3.predictCompare the two captures. The UDP version of this application was two packets. How many is the TCP version, and how many of those carry the sentence?

  4. 4.In the capture, packet 3 is an ACK carrying no data. What has been achieved by the time it arrives, and what has not?

  5. 5.The TCP server closes `connectionSocket` after each client but never closes `serverSocket`. Why the difference?

  6. 6.Three lines differ most between UDPClient.py and TCPClient.py. Which set is it?

  7. 7.The book says the three-way handshake is "completely invisible to the client and server programs". What does the capture show about that claim?

What to remember

  • The client’s connect() triggers a three-way handshake that is invisible to both programs — and costs a full round-trip time before any data moves.
  • The server has two kinds of socket. The welcoming socket is the initial point of contact for all clients, and never carries data. accept() returns a connection socket dedicated to one client, and that is the one every byte crosses.
  • The same exchange is 2 packets over UDP and 8 over TCP. Six of the eight carry no application data at all.