TCP and UDP Server using Select

When it comes to server-client communication, two protocols are commonly used: TCP and UDP. Transmission Control Protocol (TCP) is a connection-oriented protocol that ensures reliable delivery of data packets between network devices. On the other hand, User Datagram Protocol (UDP) is a connectionless protocol that offers faster data transmission but with no guarantee of delivery or order.

In this article, we will explore how to build a server using both protocols in Python programming language. We will also discuss the use of select() function in handling multiple client connections efficiently without the overhead of threading.

Setting up the Server

Creating a Socket for TCP and UDP Protocols

The first step in setting up a server is creating a socket for both TCP and UDP protocols. A socket is an endpoint that facilitates communication between different devices or processes over a network. For TCP, we use the stream-oriented protocol, which requires us to create a SOCK_STREAM socket.

On the other hand, for UDP, we use the datagram-oriented protocol and create a SOCK_DGRAM socket. In Python, we can create sockets using the built-in socket module.

import socket

# Create TCP socket
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Create UDP socket
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

Binding the Socket to a Specific IP Address and Port Number

Once we have created both sockets, we need to bind them to specific IP addresses and port numbers. Binding is necessary because it allows clients to connect to our server at the specified address and port number.

To bind our sockets in Python, we call the bind() method on each of our sockets with the IP address and port number as parameters.

# Bind TCP Socket
tcp_socket.bind(('localhost', 5000))

# Bind UDP Socket
udp_socket.bind(('localhost', 6000))

In this example, we are binding our TCP socket to port 5000 while binding our UDP socket to port 6000 on localhost.

Listening for Incoming Connections

After successfully binding our sockets to specific IP addresses and port numbers, we need to set them up to listen for incoming connections from clients. For TCP protocols, we use the listen() method on our TCP socket.

This method takes one parameter - the maximum number of queued connections - and starts listening for incoming connections.

# Listen for incoming TCP connections
tcp_socket.listen(5)

Since UDP is a connectionless protocol, there's no need to set up a listening mode. We can start receiving data from any client that sends information to our bound socket.

Handling Connections with Select()

Understanding the select() Function

The select() function is a powerful tool that can monitor multiple sockets for incoming data or connections. It is highly efficient and scalable, making it an ideal choice for handling large numbers of clients simultaneously without creating separate threads for each connection.

Select() Function Monitoring Multiple Sockets TCP Socket UDP Socket Client Socket 1 Client Socket 2 select() Function

In Python, the select() function takes three lists of sockets: readable, writable, and exceptional, plus an optional timeout value. It returns three lists containing the sockets that are ready for reading, writing, or have exceptional conditions.

import select

# Monitor sockets for activity
readable, writable, exceptional = select.select([tcp_socket, udp_socket], [], [], timeout)

Using Select() to Monitor Multiple Sockets

To use select(), we create lists of file descriptors that we want to monitor. The function will block until activity occurs on one or more of the specified sockets, making it efficient for handling multiple connections without polling.

When a socket becomes ready for reading, it indicates either a new connection request (for listening sockets) or incoming data (for established connections).

TCP Server Implementation

Establishing Connection-oriented Communication

TCP is a connection-oriented protocol that establishes a reliable communication channel between the server and clients. To establish such a connection, we create a TCP socket, bind it to an address, and use listen() to make it listen for incoming connections.

Once a client requests to connect, accept() is used to accept the connection request and return a new socket descriptor for communication with that specific client.

# Accept new TCP connection
client_socket, client_address = tcp_socket.accept()
print(f"New TCP connection from {client_address}")

Handling Multiple Client Requests with Select

Using select() allows us to handle multiple TCP clients efficiently in a single-threaded manner. We maintain a list of all active sockets and monitor them for activity.

socket_list = [tcp_socket]
while True:
    readable, _, _ = select.select(socket_list, [], [])
    for sock in readable:
        if sock == tcp_socket:  # New connection
            client_socket, addr = tcp_socket.accept()
            socket_list.append(client_socket)
        else:  # Data from existing client
            data = sock.recv(1024)
            if data:
                sock.send(data)  # Echo back
            else:
                socket_list.remove(sock)
                sock.close()

UDP Server Implementation

Establishing Connectionless Communication

In a UDP server, communication is connectionless. This means that the server does not establish a persistent connection with its clients. Instead, each client sends individual datagrams that are handled independently by the server.

To set up a UDP server, we create and bind a socket to an IP address and port number, then listen for incoming datagrams from multiple clients using recvfrom().

# Receive UDP datagram
data, client_address = udp_socket.recvfrom(1024)
print(f"Received UDP data from {client_address}: {data}")

# Send response back
udp_socket.sendto(b"Response", client_address)

Handling Datagrams with Select

A key advantage of using select() function in UDP servers is that it allows for efficient handling of datagrams from multiple clients. When a datagram arrives, select() indicates that the UDP socket is ready for reading.

while True:
    readable, _, _ = select.select([udp_socket], [], [])
    if udp_socket in readable:
        data, addr = udp_socket.recvfrom(1024)
        # Process data and send response
        response = process_udp_data(data)
        udp_socket.sendto(response, addr)

Comparison of TCP vs UDP with Select

Feature TCP with Select UDP with Select
Connection Type Connection-oriented Connectionless
Reliability Guaranteed delivery Best effort delivery
Socket Management Multiple client sockets Single server socket
Data Handling Stream-based Message-based

Conclusion

The select() function provides an efficient way to handle multiple client connections for both TCP and UDP servers without the complexity of threading. TCP offers reliable, connection-oriented communication, while UDP provides fast, connectionless messaging, and both can be effectively managed using select-based multiplexing.

Updated on: 2026-03-16T23:36:12+05:30

1K+ Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements