Fundamentals 14 min read

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.

ITPUB
ITPUB
ITPUB
Linux Socket I/O Models Explained: Blocking, Non‑Blocking, Select & Signal‑Driven

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CI/O Models
ITPUB
Written by

ITPUB

Official ITPUB account sharing technical insights, community news, and exciting events.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.