天天看点

Linux常见的网络I/0模型及同步/异步消息通知机制

作者:运维木子李

#头条创作挑战赛#

在Linux中,常见的网络I/O模型包括阻塞I/O、非阻塞I/O、多路复用I/O和异步I/O。

Linux常见的网络I/0模型及同步/异步消息通知机制

下面是对每种模型的详细说明以及同步/异步消息通知机制的示例(以C举例):

阻塞I/O:

在阻塞I/O模型中,当应用程序进行I/O操作时,它将一直阻塞(即暂停执行)直到操作完成。

示例:

// 创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

// 建立连接
connect(sockfd, (struct sockaddr*)&server_address, sizeof(server_address));

// 发送数据
send(sockfd, buffer, sizeof(buffer), 0);

// 接收数据
recv(sockfd, buffer, sizeof(buffer), 0);

// 关闭套接字
close(sockfd);           

非阻塞I/O:

在非阻塞I/O模型中,应用程序可以继续执行其他操作,而不需要等待I/O操作的完成。它通过轮询或使用select或poll函数来检查I/O操作是否就绪。

示例:

// 设置套接字为非阻塞模式
int flags = fcntl(sockfd, F_GETFL, 0);
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

// 发送数据
int send_result = send(sockfd, buffer, sizeof(buffer), 0);
if (send_result == -1 && errno == EAGAIN) {
    // 数据未就绪,稍后重试
}

// 接收数据
int recv_result = recv(sockfd, buffer, sizeof(buffer), 0);
if (recv_result == -1 && errno == EAGAIN) {
    // 数据未就绪,稍后重试
}

// 关闭套接字
close(sockfd);           

多路复用I/O:

在多路复用I/O模型中,应用程序使用select、poll或epoll等函数来监视多个文件描述符的I/O事件,一旦有就绪的文件描述符,应用程序就可以进行相应的读写操作。

示例:

// 创建套接字并绑定到指定端口
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(listen_fd, (struct sockaddr*)&server_address, sizeof(server_address));
listen(listen_fd, 5);

// 创建epoll实例并添加监听套接字
int epoll_fd = epoll_create1(0);
struct epoll_event event;
event.data.fd = listen_fd;
event.events = EPOLLIN;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event);

while (1) {
    // 等待事件
    struct epoll_event events[MAX_EVENTS];
    int num_events = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);

    // 处理事件
    for (int i = 0; i < num_events; i++) {
        if (events[i].data.fd == listen_fd && events[i].events & EPOLLIN) {
            // 接受连接
            int client_fd = accept(listen_fd, (struct sockaddr*)&client_address, &client_address_len);
            // 将客户端套接字添加到epoll实例中
            event.data.fd = client_fd;
            event.events = EPOLLIN;
            epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event);
        } else if (events[i].events & EPOLLIN) {
            // 接收数据
            int client_fd = events[i].data.fd;
            recv(client_fd, buffer, sizeof(buffer), 0);
        }
    }
}

// 关闭套接字和epoll实例
close(listen_fd);
close(epoll_fd);           

异步I/O:

在异步I/O模型中,应用程序发起I/O操作并指定回调函数,在数据就绪时,内核会通知应用程序执行相应的回调函数。

示例:

// 打开文件
int fd = open("file.txt", O_RDONLY | O_DIRECT | O_NONBLOCK);

// 发起异步读取操作
struct aiocb cb;
memset(&cb, 0, sizeof(struct aiocb));
cb.aio_fildes = fd;
cb.aio_buf = buffer;
cb.aio_nbytes = sizeof(buffer);
cb.aio_offset = 0;
cb.aio_sigevent.sigev_notify = SIGEV_SIGNAL;
cb.aio_sigevent.sigev_signo = SIGIO;
aio_read(&cb);

// 设置信号处理函数
void sigio_handler(int signo, siginfo_t *info, void *context) {
    // 读取完成,处理数据
    ssize_t num_bytes = aio_return(&cb);
    // ...
}

// 关闭文件
close(fd);           

这些示例展示了Linux中常见的网络I/O模型以及同步/异步消息通知机制。请注意,这些示例仅作为演示,实际应用中可能需要根据具体情况进行适当的修改和处理错误情况。

继续阅读