非阻塞网络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 1024
int main(void)
{
int server_sockfd;
int client_sockfd;
char ch;
int ret;
int recv_len;
char buff[BUFF_SIZE];
//用于 UNIX 系统内部通信的地址, struct sockaddr_un
struct 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 1024
int main(void)
{
int server_sockfd;
int client_sockfd;
char ch;
int ret;
int recv_len;
char buff[BUFF_SIZE];
//用于 UNIX 系统内部通信的地址, struct sockaddr_un
struct 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;
}