青岛城阳网站制作,微信公众平台官方网,大地seo视频,怎么制作网站源码Linux网络编程系列 #xff08;够吃#xff0c;管饱#xff09; 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编…Linux网络编程系列 够吃管饱 1、Linux网络编程系列之网络编程基础 2、Linux网络编程系列之TCP协议编程 3、Linux网络编程系列之UDP协议编程 4、Linux网络编程系列之UDP广播 5、Linux网络编程系列之UDP组播 6、Linux网络编程系列之服务器编程——阻塞IO模型 7、Linux网络编程系列之服务器编程——非阻塞IO模型 8、Linux网络编程系列之服务器编程——多路复用模型 9、Linux网络编程系列之服务器编程——信号驱动模型
一、什么是非阻塞IO模型 服务器非阻塞IO模型是一种服务器处理客户端连接请求的方式。在这种模型下服务器会采用异步IO方式即在一个线程中进行非阻塞IO操作以此来处理多个客户端连接请求。当一个连接请求到来时服务器会采用非阻塞的方式进行IO操作这样就不会阻塞其他请求的处理从而提高服务器的并发处理能力。另外非阻塞IO模型可以避免复杂的多线程或多进程并发模型降低服务器编程的复杂度。
二、特性 1、异步IO方式 采用异步IO方式进行IO操作不会阻塞其他客户端连接请求的处理。 2、单线程处理 整个服务器只使用一个线程进行客户端连接请求处理降低了线程切换和上下文切换的开销。 3、事件驱动 采用事件驱动的方式只有在有客户端连接请求到来时才进行处理节省了CPU资源消耗。 4、高并发 由于使用了非阻塞IO模型使得服务器能够同时处理大量的客户端连接请求提高了服务器的并发处理能力。 5、低延迟 使用非阻塞IO模型可以减少等待IO操作完成的时间降低了请求的延迟。 6、简单易用 非阻塞IO模型可以避免复杂的多线程或多进程并发模型降低服务器编程的复杂度。
三、使用场景 1、高并发场景 服务器需要处理大量的客户端连接请求需要提高服务器的并发处理能力。 2、低延迟场景 对于需要快速响应的应用场景比如实时通信、游戏等可以采用非阻塞IO模型以减少请求的延迟。 3、资源受限场景 对于资源受限的服务器比如嵌入式设备、单片机等采用非阻塞IO模型可以节省CPU和内存资源提高服务器的性价比。 4、长连接场景 对于需要维持长时间连接的应用比如推送消息、物联网等采用非阻塞IO模型可以减少连接的等待时间。 5、单机多线程场景 对于使用多线程模型的服务器应用由于线程切换和上下文切换的开销可能会导致性能瓶颈采用非阻塞IO模型可以降低这种开销。
四、模型框架通信流程 1、建立套接字。使用socket() 2、设置端口复用。使用setsockopt() 3、绑定自己的IP和端口号。使用bind() 4、设置监听。使用listen() 5、设置套接字为非阻塞状态。使用fcntl() 6、轮询接收连接请求。使用accept() 7、轮询查看活跃的客户端是否有数据到达。使用recv() 8、关闭套接字。使用close()
五、相关函数API接口 TCP通信流程常规的API那些在本系列的TCP协议里有大量展示这里省略详情可以点击本文开头的链接查看 1、设置套接字为非阻塞状态 // 5、设置监听套接字为非阻塞
int status fcntl(sockfd, F_GETFL); // 获取文件描述符状态
status | O_NONBLOCK; // 添加非阻塞状态
fcntl(sockfd, F_SETFL, status); // 设置文件描述符状态 六、案例 完成非阻塞IO模型结合TCP协议完成服务器通信演示使用nc命令模拟客户端 // 服务器非阻塞IO的案例#include stdio.h
#include stdio.h
#include stdlib.h
#include unistd.h
#include string.h
#include sys/types.h
#include sys/socket.h
#include netinet/in.h
#include netinet/ip.h
#include arpa/inet.h
#include fcntl.h#define MAX_LISTEN 50 // 最大能处理的连接数
#define SERVER_IP 192.168.64.128 // 记得改为自己IP
#define SERVER_PORT 20000 // 不能超过65535也不要低于1000防止端口误用// 定义客服端管理类
struct ClientManager
{int client[MAX_LISTEN]; // 存储客户端的套接字char ip[MAX_LISTEN][20]; // 客户端套接字IPuint16_t port[MAX_LISTEN]; // 客户端套接字端口号int active_client_number; // 活跃的客户端数量
};// 初始化客户端管理类
void client_manager_init(struct ClientManager *manager)
{for(int i 0; i MAX_LISTEN; i){manager-client[i] -1;manager-port[i] 0;memset(manager-ip, 0, sizeof(manager-ip));}manager-active_client_number 0;
}int main(int argc, char *argv[])
{// 1、建立套接字指定IPV4网络地址TCP协议int sockfd socket(AF_INET, SOCK_STREAM, 0);if(sockfd -1){perror(socket fail);return -1;}// 2、设置端口复用推荐int optval 1; // 这里设置为端口复用所以随便写一个值int ret setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, optval, sizeof(optval));if(ret -1){perror(setsockopt fail);close(sockfd);return -1;}// 3、绑定自己的IP地址和端口号不可以省略struct sockaddr_in server_addr {0};socklen_t addr_len sizeof(struct sockaddr);server_addr.sin_family AF_INET; // 指定协议为IPV4地址协议server_addr.sin_port htons(SERVER_PORT); // 端口号server_addr.sin_addr.s_addr inet_addr(SERVER_IP); // IP地址ret bind(sockfd, (struct sockaddr*)server_addr, addr_len);if(ret -1){perror(bind fail);close(sockfd);return -1;}// 4、设置监听ret listen(sockfd, MAX_LISTEN);if(ret -1){perror(listen fail);close(sockfd);return -1;}// 5、设置监听套接字为非阻塞int status fcntl(sockfd, F_GETFL); // 获取文件描述符状态status | O_NONBLOCK; // 添加非阻塞状态fcntl(sockfd, F_SETFL, status); // 设置文件描述符状态int i;uint16_t port 0; // 新的客户端的端口号char ip[20] {0}; // 新的客户端的IPchar recv_msg[128] {0}; // 接收数据缓冲区struct sockaddr_in client_addr; // 客户端的地址struct ClientManager manager;client_manager_init(manager); // 初始化一个管理类printf(wait client connect...\n);while(1){// 6、接受连接请求int new_client_fd accept(sockfd, (struct sockaddr*)client_addr, addr_len);if(new_client_fd ! -1){memset(ip, 0, sizeof(ip));strcpy(ip, inet_ntoa(client_addr.sin_addr));port ntohs(client_addr.sin_port);printf([%s:%d] connect\n, ip, port);// 新连接的套接字也要设置为非阻塞状态status fcntl(new_client_fd, F_GETFL); // 获取文件描述符状态status | O_NONBLOCK; // 添加非阻塞状态fcntl(new_client_fd, F_SETFL, status); // 设置文件描述符状态// 把连接上来的客户端套接字加入管理类manager.client[manager.active_client_number] new_client_fd;manager.port[manager.active_client_number] port;strcpy(manager.ip[manager.active_client_number], ip);manager.active_client_number;}// 7、轮询方式查看连接上来的客户端是否有数据到达非阻塞方式不会等待for(i 0; i manager.active_client_number;){// 尝试接收数据memset(recv_msg, 0, sizeof(recv_msg));ret recv(manager.client[i], recv_msg, sizeof(recv_msg), 0);// 客户端断开if(ret 0){printf([%s:%d] disconnet\n, manager.ip[i], manager.port[i]);for(int j i 1; j manager.active_client_number; j){// 把活跃的套接字往前移一位if(manager.client[j] ! -1){manager.client[j-1] manager.client[j];manager.port[j-1] manager.port[j];strcpy(manager.ip[j-1], manager.ip[j]);}}// 更新活跃的套接字数量注意不需要imanager.active_client_number--;// 最后一个要清空manager.client[manager.active_client_number] -1;manager.port[manager.active_client_number] 0;memset(manager.ip[manager.active_client_number], 0, sizeof(ip));}else if(ret 0){printf([%s:%d] send data: %s\n, manager.ip[i], manager.port[i], recv_msg);i; // 这需要i;,上面不用}else{i; // 这里也需要i没有数据时需要询问下一个}}}// 7、关闭套接字close(sockfd);return 0;
} 七、总结 非阻塞模型适用于资源有限的需要高并发低延迟的场景。非阻塞模型TCP服务器的通信流程跟普通的TCP服务器通信流程大致相同区别在于不仅要设置服务器监听的套接字设置为非阻塞而且要把客户端的套接字也设置为非阻塞。可以结合案例加深理解。