Linux Socket I/O Models Explained: Blocking, Non‑Blocking, Select & Signal‑Driven
This article uses a mailbox analogy to explain Linux socket I/O models—synchronous blocking, synchronous non‑blocking, I/O multiplexing with select, and signal‑driven I/O—providing detailed C and Perl code examples and discussing each model's advantages and drawbacks.
Introduction
The article illustrates Linux socket I/O models with a story about a father (old Chen) receiving letters, mapping the mailbox operations to various I/O strategies. It then presents concrete C and Perl code for each model, explaining how they work and their trade‑offs.
Synchronous Blocking Model
In this classic model the process calls connect, send, and recv and blocks until each operation completes. The program does nothing while waiting, which makes the implementation simple and resource‑light, but it requires a dedicated thread for each connection.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#define SERVPORT 8080
#define MAXDATASIZE 100
int main(int argc, char *argv[]){
int sockfd, recvbytes;
char rcv_buf[MAXDATASIZE];
char snd_buf[MAXDATASIZE];
struct sockaddr_in server_addr;
if(argc < 3){
printf("Usage:%s [ip] [msg]
", argv[0]);
return 1;
}
strcpy(snd_buf, argv[2]);
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
perror("socket:"); exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVPORT);
inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
memset(&(server_addr.sin_zero),0,8);
if(connect(sockfd,(struct sockaddr*)&server_addr,sizeof(struct sockaddr))==-1){
perror("connect"); exit(1);
}
if(send(sockfd,snd_buf,sizeof(snd_buf),0)==-1){
perror("send:"); exit(1);
}
if((recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, 0))==-1){
perror("recv:"); exit(1);
}
rcv_buf[recvbytes] = '\0';
printf("recv:%s
", rcv_buf);
close(sockfd);
return 0;
}Synchronous Non‑Blocking Model
Here the socket is set to non‑blocking mode (using fcntl with O_NONBLOCK). System calls return immediately with EAGAIN or EWOULDBLOCK if the operation cannot be completed, so the application must retry until success. This reduces idle waiting but can lead to busy‑polling and lower throughput.
#include <fcntl.h>
... // same includes as before
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, flags | O_NONBLOCK);
while(send(sockfd, snd_buf, sizeof(snd_buf), MSG_DONTWAIT) == -1){
sleep(10);
printf("sleep
");
}
while((recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, MSG_DONTWAIT)) == -1){
sleep(10);
printf("sleep
");
}I/O Multiplexing (Select) – Asynchronous Blocking Model
In this model the socket remains non‑blocking, but the process uses the select system call to block until one or more file descriptors become ready. select can monitor many sockets simultaneously, notifying the application when data can be read or written.
#include <sys/select.h>
fd_set readset, writeset;
int maxfd, ret;
int fp = open("data_from_socket.txt", O_WRONLY);
while(1){
FD_ZERO(&readset);
FD_SET(sockfd, &readset);
FD_ZERO(&writeset);
FD_SET(fp, &writeset);
maxfd = (sockfd > fp ? sockfd : fp) + 1;
ret = select(maxfd, &readset, NULL, NULL, NULL); // blocks indefinitely
if(ret > 0 && FD_ISSET(sockfd, &readset)){
recvbytes = recv(sockfd, rcv_buf, MAXDATASIZE, MSG_DONTWAIT);
rcv_buf[recvbytes] = '\0';
printf("recv:%s
", rcv_buf);
if(FD_ISSET(fp, &writeset))
write(fp, rcv_buf, strlen(rcv_buf));
break; // exit after first successful read
}
}
close(fp);
close(sockfd);Signal‑Driven I/O Model
Signal‑driven I/O registers a handler for SIGIO. The socket is configured with fcntl(fd, F_SETFL, O_ASYNC) (or similar) so that the kernel sends a signal when the descriptor becomes ready. The process can continue other work while waiting, and the handler performs a non‑blocking recvfrom or notifies the main loop.
struct sigaction sa;
sa.sa_handler = sigio_handler;
sigaction(SIGIO, &sa, NULL);
fcntl(sockfd, F_SETFL, O_ASYNC | O_NONBLOCK);Perl Example
A compact Perl client using IO::Socket::INET demonstrates a blocking connection and simple send/receive logic.
use IO::Socket;
my $sock = IO::Socket::INET->new(
PeerAddr => "192.168.1.73",
PeerPort => 8080,
Proto => "tcp",
Blocking => 1
) or die "Cannot create socket: $@";
$sock->send("Hello server!
");
$sock->autoflush(1);Conclusion
Blocking I/O is easy but scales poorly; non‑blocking reduces idle time but may cause busy‑waiting; select (or newer mechanisms like epoll) enables efficient multiplexing; signal‑driven I/O offers asynchronous notification without a dedicated thread. Understanding these models helps developers choose the right strategy for high‑performance network programs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
ITPUB
Official ITPUB account sharing technical insights, community news, and exciting events.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
