Appearance
TCP UDP System Calls
TCP/UDP System Calls
The foundation of network programming in Unix-like systems is the socket API. A socket is a communication endpoint that allows processes to exchange data across networks or within the same machine. This is the Programmer's interface to the socket layer provided to them by the Operating System.
socket() - Creating Communication Endpoints
The socket() system call creates a new communication endpoint and returns a file descriptor:
cpp
int socket(int domain, int type, int protocol);Parameters:
domain: Communication domain (AF_INET for IPv4, AF_INET6 for IPv6, AF_UNIX for local)type: Socket type (SOCK_STREAM for TCP, SOCK_DGRAM for UDP)protocol: Protocol (usually 0 for default)
Flags:
SOCK_NONBLOCK: Creates a non-blocking socketSOCK_CLOEXEC: Close-on-exec flag for security
bind() - Associating Sockets with Addresses
The bind() system call associates a socket with a specific address and port:
cpp
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);This is essential for servers to specify which port they'll listen on. Clients typically don't need to bind to a specific address.
listen() - Preparing for Connections
For TCP servers, listen() marks the socket as passive and starts listening for incoming connections:
cpp
int listen(int sockfd, int backlog);The backlog parameter specifies the maximum length of the pending connections queue.
accept() - Accepting Connections
The accept() system call extracts the first connection from the listening queue and creates a new socket for that connection:
cpp
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);This is how servers handle multiple clients - each accepted connection gets its own socket.
connect() - Initiating Connections
Clients use connect() to establish a connection to a server:
cpp
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);For TCP, this initiates the three-way handshake. For UDP, this simply sets the default destination.
close() - Closing Connections
The close() system call closes the socket and releases associated resources:
cpp
int close(int sockfd);send() and recv() - TCP Data Transfer
For TCP sockets, use send() and recv() for reliable data transfer:
cpp
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);Common flags:
MSG_DONTWAIT: Non-blocking operationMSG_NOSIGNAL: Don't generate SIGPIPEMSG_OOB: Out-of-band data
sendto() and recvfrom() - UDP Data Transfer
For UDP sockets, use sendto() and recvfrom() for connectionless communication:
cpp
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);setsockopt() - Configuring Sockets
The setsockopt() system call allows you to configure various socket options:
cpp
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);Common options:
SO_REUSEADDR: Allow binding to an address that's already in useSO_KEEPALIVE: Enable TCP keep-alive mechanismSO_LINGER: Control socket closure behaviorSO_RCVBUF/SO_SNDBUF: Set receive/send buffer sizes
getsockopt() - Retrieving Socket Options
The getsockopt() system call retrieves current socket option values:
cpp
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);Non-blocking Sockets
Creating Non-blocking Sockets
You can create non-blocking sockets in several ways:
- During creation:
cpp
int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);- After creation:
cpp
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);Handling Non-blocking Operations
Non-blocking sockets return EAGAIN or EWOULDBLOCK when operations would block:
cpp
ssize_t bytes = recv(sockfd, buffer, size, 0);
if (bytes == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
// No data available, try again later
}Socket Errors and Debugging
Common Socket Errors
EADDRINUSE: Address already in useECONNREFUSED: Connection refused by peerETIMEDOUT: Connection timed outENOBUFS: No buffer space availableEMSGSIZE: Message too large
Socket State Checking
Use getsockopt() with SO_ERROR to check for pending errors:
cpp
int error = 0;
socklen_t len = sizeof(error);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len);
if (error != 0) {
// Handle error
}Buffer Sizing
Proper buffer sizing is crucial for performance:
cpp
int rcvbuf = 1024 * 1024; // 1MB receive buffer
setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));TCP_NODELAY
For low-latency applications, disable Nagle's algorithm:
cpp
int flag = 1;
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));Socket Reuse
Enable address reuse to avoid "Address already in use" errors:
cpp
int reuse = 1;
setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));Socket Flow Patterns
TCP Socket Flow
socket(AF_INET, SOCK_STREAM, 0)- Create TCP socketbind()- Bind to address (server)listen()- Start listening (server)accept()- Accept connections (server)connect()- Connect to server (client)send()/recv()- Data transferclose()- Close connection
UDP Socket Flow
socket(AF_INET, SOCK_DGRAM, 0)- Create UDP socketbind()- Bind to address (optional for client)sendto()/recvfrom()- Data transferclose()- Close socket
The key difference is that UDP doesn't require connection establishment - you can immediately start sending data with sendto().
File Descriptor Management
Sockets are file descriptors, so they can be used with:
select(),poll(),epoll()- Multiplexingfcntl()- File descriptor controldup(),dup2()- Duplicationclose()- Closure
This integration with the file descriptor system makes sockets compatible with all standard Unix I/O operations and multiplexing mechanisms.
Questions
Q: Which system call is used to create a new socket?
socket() creates a new communication endpoint and returns a file descriptor.
Q: What does the bind() system call do?
bind() associates a socket with a specific address and port number.
Q: Which flag in socket() creates a non-blocking socket?
SOCK_NONBLOCK flag creates a non-blocking socket for asynchronous I/O.
Q: What is the purpose of setsockopt() with SO_REUSEADDR?
SO_REUSEADDR allows binding to an address that is already in use.
Q: What is the difference between TCP and UDP sockets?
TCP provides reliability through acknowledgments and retransmission, while UDP is connectionless.
Q: Which system call is used to accept incoming connections?
accept() extracts the first connection from the listening queue and creates a new socket.
Q: What does the listen() system call do?
listen() marks the socket as passive and starts listening for incoming connections.
Q: Which flag enables TCP keep-alive?
SO_KEEPALIVE enables TCP keep-alive mechanism to detect dead connections.