Learn Steps

Understanding TCP and UDP: A Deep Dive into Sockets

Inter-Process Communication (IPC) allows processes with separate contexts to communicate. Common IPC methods include pipes, FIFOs, and message queues. However, what if processes are on different hosts?

Sockets provide data exchange between applications on the same or different hosts connected via a network.

In a client-server scenario, applications use sockets to communicate. Here’s how it works:

Each application creates a socket. This step is essential for any app that wants to communicate.

The server binds its socket to an address and a port. This binding allows clients to locate the server.

Sockets come in two types: stream and datagram. Both types are supported in UNIX and Internet domains.

Stream Sockets (SOCK_STREAM)

Reliable: They ensure data arrives intact or notify us of a failure.

Bidirectional: Data can flow in either direction between two sockets.

Byte-stream: There are no message boundaries.

Datagram Sockets (SOCK_DGRAM)

Datagrams: They exchange data as messages called datagrams.

Boundaries: They preserve message boundaries but are not reliable.

Not Reliable: Messages may arrive out of order, be duplicated, or not arrive at all. Datagram sockets are connectionless.

In the Internet domain, datagram sockets use the User Datagram Protocol (UDP). On the other hand, stream sockets typically use the Transmission Control Protocol (TCP).

Socket System Calls

The socket() call creates a new socket and returns a file descriptor.

The bind() call binds a socket to an address and a port. Servers use this call to make their sockets available to clients.

The listen() call allows a stream socket to accept incoming connections.

The accept() call accepts a connection from a peer application on a listening socket.

The connect() call establishes a connection with another socket.

When the application calls listen(), the TCP stack performs the 3-way handshake for incoming connections. The kernel queues these connections, and accept() retrieves the next one.

The backlog argument to listen() specifies the queue size. When the queue is full, the stack stops performing handshakes for new connections. Consequently, clients need to retry until there is room in the queue.

This approach ensures that clients receive the SYN/ACK quickly, assuming the backlog queue has space. Thus, clients do not have to retransmit the SYN.