Skip to content
Published on

[Computer Networking] 07. Socket Programming: TCP and UDP

Authors

This post is based on the textbook Computer Networking: A Top-Down Approach (6th Edition) by James Kurose and Keith Ross.


1. Socket Programming Overview

Network applications consist of communication between two processes (client and server). The point of contact for this communication is the socket.

+--------------+                    +--------------+
| Application  |                    | Application  |
|  Process     |                    |  Process     |
|      |       |                    |      |       |
|  Socket(door)|                    |  Socket(door)|
+------+-------+    Internet        +------+-------+
| Transport    | <===============> | Transport    |
+--------------+                    +--------------+

A socket is the interface (API) between the application layer and the transport layer.

1.1 Two Transport Services

ServiceTCPUDP
ConnectionConnection-orientedConnectionless
ReliabilityReliable (order guaranteed, retransmission)Unreliable (loss possible)
Flow controlYesNo
Congestion controlYesNo
OverheadHighLow

2. UDP Socket Programming

2.1 Characteristics of UDP Communication

  • No connection setup: No handshaking needed before sending data
  • Destination IP and port number must be explicitly attached to each datagram
  • No packet order guarantee, packet loss possible

2.2 UDP Client-Server Interaction Flow

    Server                          Client
    ------                          ------
  Create socket                    Create socket
  (serverSocket)                   (clientSocket)
       |                              |
  Bind address                        |
  (IP, port)                          |
       |                              |
  Wait to receive <--- Datagram --- Send message
  (recvfrom)      (dest IP:port)    (sendto)
       |                              |
  Process data                   Wait to receive
       |                         (recvfrom)
  Send response ---- Datagram -----> Receive response
  (sendto)                            |
       |                         Close socket

2.3 UDP Server Code (Python)

from socket import *

serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
print("UDP server ready. Port:", serverPort)

while True:
    # Receive message from client
    message, clientAddress = serverSocket.recvfrom(2048)
    print(f"Received: {message.decode()} from {clientAddress}")

    # Convert to uppercase and respond
    modifiedMessage = message.decode().upper()
    serverSocket.sendto(modifiedMessage.encode(), clientAddress)

Code Explanation

socket(AF_INET, SOCK_DGRAM)
  AF_INET: IPv4
  SOCK_DGRAM: UDP socket

bind(('', serverPort))
  '': Receive on all interfaces
  serverPort: Port number to bind

recvfrom(2048)
  Receive up to 2048 bytes
  Returns: (data, client address)

sendto(data, address)
  Send data to the specified address

2.4 UDP Client Code (Python)

from socket import *

serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)

message = input("Enter a lowercase sentence: ")
clientSocket.sendto(message.encode(), (serverName, serverPort))

# Receive server response
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
print("Server response:", modifiedMessage.decode())

clientSocket.close()

Execution Example

# Server terminal
$ python UDPServer.py
UDP server ready. Port: 12000
Received: hello world from ('127.0.0.1', 54321)

# Client terminal
$ python UDPClient.py
Enter a lowercase sentence: hello world
Server response: HELLO WORLD

3. TCP Socket Programming

3.1 Characteristics of TCP Communication

  • Connection-oriented: TCP connection must be established before data transfer
  • Distinction between welcome (listening) socket and connection socket
  • Byte-stream based (no message boundaries)
  • Reliable delivery: order guaranteed, retransmission

3.2 TCP Client-Server Interaction Flow

    Server                          Client
    ------                          ------
  Create welcome socket                 |
  (serverSocket)                        |
       |                                |
  Bind address                          |
  listen()                              |
       |                          Create socket
  accept() waiting <-- TCP conn req -- connect()
       |          (3-way handshake)     |
  Create connection socket              |
  (connectionSocket)                    |
       |                                |
  recv() <----------- Data ---------- send()
       |                                |
  Process data                          |
       |                                |
  send() ----------- Response -------> recv()
       |                                |
  Close connection socket          Close socket
  (connectionSocket.close())            |
       |
  Wait for next connection...

Key Point: Two Types of Sockets

Server-side sockets:

  1. Welcome Socket (Listening Socket)
     - serverSocket: The door that accepts client connection requests
     - Used with accept() call

  2. Connection Socket
     - connectionSocket: A dedicated socket for a specific client
     - New socket returned by accept()
     - Used for data transmission

3.3 TCP Server Code (Python)

from socket import *

serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(1)
print("TCP server ready. Port:", serverPort)

while True:
    # Accept client connection (blocking)
    connectionSocket, clientAddr = serverSocket.accept()
    print(f"Connection established: {clientAddr}")

    # Receive data
    message = connectionSocket.recv(1024).decode()
    print(f"Received: {message}")

    # Convert to uppercase and respond
    capitalizedMessage = message.upper()
    connectionSocket.send(capitalizedMessage.encode())

    # Close connection socket (welcome socket remains open)
    connectionSocket.close()

Code Explanation

socket(AF_INET, SOCK_STREAM)
  SOCK_STREAM: TCP socket

listen(1)
  Allow up to 1 queued connection
  (backlog queue size)

accept()
  Blocks until a connection request arrives
  Returns: (new connection socket, client address)

recv(1024)
  Receive up to 1024 bytes
  TCP is a byte stream, so no boundaries

send(data)
  Send data over the connected socket
  No need to specify destination address (already connected)

3.4 TCP Client Code (Python)

from socket import *

serverName = '127.0.0.1'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)

# Establish TCP connection (3-way handshake)
clientSocket.connect((serverName, serverPort))

message = input("Enter a lowercase sentence: ")
clientSocket.send(message.encode())

# Receive server response
modifiedMessage = clientSocket.recv(1024)
print("Server response:", modifiedMessage.decode())

clientSocket.close()

Execution Example

# Server terminal
$ python TCPServer.py
TCP server ready. Port: 12000
Connection established: ('127.0.0.1', 54322)
Received: hello tcp world

# Client terminal
$ python TCPClient.py
Enter a lowercase sentence: hello tcp world
Server response: HELLO TCP WORLD

4. UDP vs TCP Socket Programming Comparison

4.1 Code-Level Differences

ItemUDPTCP
Socket typeSOCK_DGRAMSOCK_STREAM
Connection setupNoneconnect() / accept()
Data sendsendto(data, addr)send(data)
Data receiverecvfrom(bufsize)recv(bufsize)
Address specificationEvery timeOnce at connection
Server socketsSingle socketWelcome + connection socket

4.2 Flow Comparison Diagram

UDP:                              TCP:
----                              ----
Server: socket()                  Server: socket()
Server: bind()                    Server: bind()
                                  Server: listen()
Client: socket()                  Server: accept() (blocking)
Client: sendto() ---------->
                                  Client: socket()
Server: recvfrom()                Client: connect() ------>
Server: sendto() ---------->                   3-way handshake
                                  Server: (new connection socket returned)
Client: recvfrom()
Client: close()                   Client: send() ---------->
                                  Server: recv()
                                  Server: send() ---------->
                                  Client: recv()
                                  Client: close()
                                  Server: close() (connection socket)

5. Multi-Threaded TCP Server

The basic TCP server handles only one client at a time. To handle multiple clients simultaneously, multi-threading is used.

from socket import *
import threading

def handle_client(connectionSocket, addr):
    """Thread function to handle each client"""
    print(f"Thread started: {addr}")
    try:
        message = connectionSocket.recv(1024).decode()
        response = message.upper()
        connectionSocket.send(response.encode())
    finally:
        connectionSocket.close()
        print(f"Thread ended: {addr}")

serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(5)
print("Multi-threaded TCP server ready")

while True:
    connectionSocket, addr = serverSocket.accept()
    # Handle client in a new thread
    thread = threading.Thread(
        target=handle_client,
        args=(connectionSocket, addr)
    )
    thread.start()
Multi-threaded server operation:

  Client1 --connect--> [Welcome socket] --accept()--> [Conn socket 1] --Thread 1
  Client2 --connect--> [Welcome socket] --accept()--> [Conn socket 2] --Thread 2
  Client3 --connect--> [Welcome socket] --accept()--> [Conn socket 3] --Thread 3

  Each client is handled in an independent thread

6. Summary

Socket programming key summary:

  UDP Socket:
  +-- Connectionless, datagram-based
  +-- sendto/recvfrom (specify address every time)
  +-- Fast but unreliable
  +-- Use cases: DNS, streaming, games

  TCP Socket:
  +-- Connection-oriented, byte stream
  +-- connect/accept to establish connection
  +-- send/recv (no address specification needed)
  +-- Reliable but has overhead
  +-- Use cases: Web, email, file transfer

7. Review Questions

Q1. Why does a UDP socket use sendto()?

UDP is a connectionless protocol, so no connection is established in advance. Therefore, the destination IP address and port number must be specified with each datagram sent. It uses the form sendto(data, (ip, port)) to specify the destination. In contrast, TCP already has an established connection via connect(), so send(data) alone is sufficient.

Q2. What is the difference between the welcome socket and the connection socket in a TCP server?
  • Welcome socket (serverSocket): The socket that accepts client connection requests. Used with listen() and accept(). Remains open as long as the server is running.
  • Connection socket (connectionSocket): A new socket returned by accept(). Used for data transmission with a specific client. Closed when communication is complete.

This separation allows the server to communicate with multiple clients simultaneously.

Q3. How does TCP being a byte stream affect programming?

TCP does not maintain message boundaries. Even if send() sends 100 bytes, there is no guarantee that recv() will receive all 100 bytes at once. It might receive 50 bytes in two separate calls. Therefore, the application must handle message boundaries itself (e.g., using delimiters, adding a length header, etc.).