硬核图解网络IO模型!
原创硬核图解网络IO模型
在网络编程中,IO(输入/输出)操作是不可避免的。无论是读取文件、接收网络数据还是写入数据,IO操作都是程序与外部世界交互的桥梁。网络IO模型是懂得网络编程核心原理的关键,本文将硬核图解网络IO模型,帮助读者深入懂得其工作原理。
1. IO模型概述
IO模型关键描述了程序在执行IO操作时的状态转换和执行流程。常见的IO模型有五种:阻塞IO、非阻塞IO、IO多路复用、信号驱动IO和异步IO。
2. 阻塞IO
阻塞IO是传统IO模型,程序执行IO操作时,当前线程会阻塞,等待IO操作完成。以下是一个易懂的阻塞IO示例:
#include <stdio.h>
#include <unistd.h>
int main() {
int fd = open("example.txt", O_RDONLY);
if (fd == -1) {
perror("open");
return 1;
}
char buffer[1024];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
if (bytes_read == -1) {
perror("read");
close(fd);
return 1;
}
printf("Read %ld bytes: %s ", bytes_read, buffer);
close(fd);
return 0;
}
在上述代码中,程序执行read操作时,如果文件内容不足1024字节,当前线程会阻塞,直到读取到足够的数据或出现失误。
3. 非阻塞IO
非阻塞IO允许程序在IO操作未完成时继续执行其他任务。以下是一个非阻塞IO示例:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main() {
int fd = open("example.txt", O_RDONLY | O_NONBLOCK);
if (fd == -1) {
perror("open");
return 1;
}
char buffer[1024];
ssize_t bytes_read;
while ((bytes_read = read(fd, buffer, sizeof(buffer))) == -1 && errno == EAGAIN);
if (bytes_read == -1) {
perror("read");
close(fd);
return 1;
}
printf("Read %ld bytes: %s ", bytes_read, buffer);
close(fd);
return 0;
}
在上述代码中,通过设置O_NONBLOCK标志,read操作不会阻塞当前线程。如果IO操作未完成,read会返回-1,并设置errno为EAGAIN,程序可以继续执行其他任务。
4. IO多路复用
IO多路复用允许程序同时监听多个IO操作,当任何一个IO操作完成时,程序可以立即得到通知并处理。epoll是Linux系统中常用的IO多路复用机制。以下是一个使用epoll的IO多路复用示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#define PORT 8080
int main() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
if (listen_fd == -1) {
perror("socket");
return 1;
}
struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {
perror("bind");
close(listen_fd);
return 1;
}
if (listen(listen_fd, 5) == -1) {
perror("listen");
close(listen_fd);
return 1;
}
int epoll_fd = epoll_create1(0);
if (epoll_fd == -1) {
perror("epoll_create1");
close(listen_fd);
return 1;
}
struct epoll_event event;
event.data.fd = listen_fd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event);
struct sockaddr_in client_addr;
socklen_t client_addr_len = sizeof(client_addr);
int accept_fd;