非阻塞网络IO模式介绍
当用户线程发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个 error 时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。
所以事实上,在非阻塞 IO 模型中,用户线程需要不断地询问内核数据是否就绪,也就 说非阻塞 IO 不会交出 CPU,而会一直占用 CPU。
设置非阻塞常用方式:
方式一: 创建 socket 时指定
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
方式二: 在使用前通过如下方式设定
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);
方式一 demo
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>#define BUFF_SIZE 1024int main(void)
{int server_sockfd;int client_sockfd;char ch;int ret;int recv_len;char buff[BUFF_SIZE];//用于 UNIX 系统内部通信的地址, struct sockaddr_unstruct sockaddr_in server_addr;struct sockaddr_in client_addr;int client_addr_len =sizeof(struct sockaddr_in);server_sockfd = socket(AF_INET, SOCK_DGRAM|SOCK_NONBLOCK, 0);// 设置服务器地址server_addr.sin_family = AF_INET; //地址的域,相当于地址的类型,AF_UNIX 表示地址位于 UNIX 系统内部server_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr("10.10.0.9");server_addr.sin_port = htons(9000);// 绑定该套接字,使得该套接字和对应的系统套接字文件关联起来。ret = bind(server_sockfd, (struct sockaddr*)&server_addr,sizeof(server_addr));if (ret == -1) {perror("bind");exit(1);}// 创建套接字队列, 保存进入该服务器的客户端请求。// ret = listen(server_sockfd, 5);// 循环处理客户端请求while (1) {printf("server waiting\n");// 等待并接收客户端请求//client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_addr_len);recv_len = recvfrom(server_sockfd, buff, sizeof(buff) , 0,(struct sockaddr*)&client_addr, &client_addr_len);if (recv_len < 0) {if(errno == EAGAIN ||errno == EWOULDBLOCK){sleep(2);continue;}perror("recvfrom");exit(errno);}printf("received: %s\n", buff);}close(server_sockfd);return 0;
}
方式二 demo
#include <sys/un.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>#define BUFF_SIZE 1024int main(void)
{int server_sockfd;int client_sockfd;char ch;int ret;int recv_len;char buff[BUFF_SIZE];//用于 UNIX 系统内部通信的地址, struct sockaddr_unstruct sockaddr_in server_addr;struct sockaddr_in client_addr;int client_addr_len =sizeof(struct sockaddr_in);server_sockfd = socket(AF_INET, SOCK_DGRAM, 0);// 设置服务器地址server_addr.sin_family = AF_INET; //地址的域,相当于地址的类型,AF_UNIX 表示地址位于 UNIX 系统内部server_addr.sin_addr.s_addr = INADDR_ANY; //inet_addr("10.10.0.9");server_addr.sin_port = htons(9000);// 绑定该套接字,使得该套接字和对应的系统套接字文件关联起来。ret = bind(server_sockfd, (struct sockaddr*)&server_addr,sizeof(server_addr));if (ret == -1) {perror("bind");exit(1);}// 创建套接字队列, 保存进入该服务器的客户端请求。// ret = listen(server_sockfd, 5);fcntl(server_sockfd, F_SETFL, fcntl(server_sockfd, F_GETFL, 0) | O_NONBLOCK);// 循环处理客户端请求while (1) {printf("server waiting\n");// 等待并接收客户端请求//client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_addr, &client_addr_len);recv_len = recvfrom(server_sockfd, buff, sizeof(buff) , 0,(struct sockaddr*)&client_addr, &client_addr_len);if (recv_len < 0) {if(errno == EAGAIN ||errno == EWOULDBLOCK){sleep(2);continue;}perror("recvfrom");exit(errno);}printf("received: %s\n", buff);}close(server_sockfd);return 0;
}