TCP Overview
The Transmission Control Protocol (TCP) is a reliable, connection-oriented protocol operating at the transport layer. Defined by IETF's RFC 793, TCP ensures data integrity through sequencing, acknowledgment, and retransmission mechanisms.
Communication using TCP requires three stages: establishing a connection, transferring data, and terminating the connection.
Key Characteristics of TCP
Connection-Oriented Nature
Before data transfer begins, both parties must establish a communication channel. This is analogous to making a phone call before exchanging information. Each connection involves allocating system resources for managing state and transmission.
Once established, all data exchanges occur over this single channel. After communication completes, the connection must be closed to free up system resources. TCP supports one-to-one connections exclusively, making it unsuitable for broadcast scenarios which prefer UDP.
Byte Stream Based
Data Handling in TCP
When an application writes multiple times to a TCP socket, these bytes are queued into a send buffer. The TCP stack then packages them into segments based on network constraints like MTU limits.
Segments sent may not correspond directly to write operations. Similarly, received segments can be read in chunks depending on the application's receive buffer size.
Comparison with UDP
In contrast, UDP sends each write operation as a single datagram. If a datagram is too large for the link layer, it will be fragmented at the IP level. Also, missing datagrams are discarded unless explicitly handled by the application.
Reliability Mechanisms
-
Acknowledgement System: Every segment must be acknowledged before being considered successfully transmitted.
-
Retransmission Timeout: If no ACK arrives within a timeout period, the segment is resent.
-
Error Checking: A checksum validates data integrity during transmission.
-
Flow Control and Congestion Avoidance: Prevents sender from overwhelming receiver with data faster than it can process.
TCP vs UDP
| Feature | TCP | UDP |
|---|---|---|
| Connection | Required | Not required |
| Ordering | Guaranteed | No order guarantee |
| Reliability | Reliable | Unreliable |
| Overhead | Higher | Lower |
| Use Case | Applications requiring delivery assurance |
Three-Way Handshake
TCP establishes a connection using a three-step handshake:
- Client → Server: SYN packet with initial sequence number (e.g., 1000).
- Server → Client: SYN+ACK packet acknowledging client’s SYN and sending its own initial sequence number (e.g., 8000).
- Client → Server: ACK packet confirming server’s SYN.
During this process, both sides exchange sequence numbers and maximum segment sizes.
If a host receives a SYN for a non-existent port, it responds with a RST packet indicating refusal.
Data Transfer Process
After connection establishment:
- Client sends data starting from sequence number 1001.
- Server acknowledges receipt and sends back data starting from sequence number 8001.
- Client confirms server’s data and requests further transmission.
Each segment includes sequence numbers and acknowledgments ensuring ordered delivery.
Four-Way Handshake for Termination
Since TCP supports full-duplex communication, each direction needs independent closure:
- Client → Server: FIN flag signals termination request.
- Server → Client: ACK confirms receipt of FIN.
- Server → Client: FIN flag initiates server-side shutdown.
- Client → Server: ACK confirms server’s FIN.
This allows half-closed states where one side can still send while the other has stopped.
Implementation Examples
Basic TCP Server
import socket
# Create socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Bind address
server_socket.bind(('', 8000))
# Listen for connections
server_socket.listen(128)
# Accept client connection
client_socket, client_address = server_socket.accept()
print(f"Connected to {client_address}")
# Receive data
received = client_socket.recv(1024)
print(f"Received: {received.decode()}")
# Send response
client_socket.send("Thank you!\n".encode())
# Close connections
client_socket.close()
server_socket.close()
Basic TCP Client
import socket
# Create client socket
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect to server
client_socket.connect(('127.0.0.1', 8000))
# Send message
message = input("Enter message: ")
client_socket.send(message.encode())
# Receive response
response = client_socket.recv(1024)
print(f"Server response: {response.decode()}")
# Close connection
client_socket.close()
Connection States
- CLOSED: Initial state
- LISTEN: Waiting for incoming connections
- SYN_SENT: Sent SYN, waiting for ACK+SYN
- SYN_RCVD: Received SYN, sent ACK+SYN
- ESTABLISHED: Connection ready
- FIN_WAIT_1: Sent FIN, waiting for ACK
- FIN_WAIT_2: Sent ACK, waiting for FIN
- TIME_WAIT: Waited for final ACK
- CLOSE_WAIT: Received FIN, waiting to close
- LAST_ACK: Final ACK being sent
- CLOSING: Both sides closing simultaneously
TIME_WAIT Considerations
The TIME_WAIT state lasts for twice the Maximum Segment Lifetime (2MSL). It prevents:
- Retransmitted segments from interfering with new connections.
- Lost duplicate packets causing confusion in new connections.
To avoid issues with server ports in TIME_WAIT, set SO_REUSEADDR option:
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
Long vs Short Connections
Short Connection
Each operation involves:
- Establishing connection
- Sending/receiving data
- Closing connection
Best for infrequent communications due to overhead.
Long Connection
- Establish connection once
- Multiple data exchanges
- Close when done
Ideal for frequent interactions where setup costs outweigh benefits.
Applications typically use long connections for database connections and short ones for web services.