- IO多路复用实现TCP并发服务器-select
- 前言
- 原理
- 代码实现
- 服务器—01server.c
- 客户端---02client.c
- 执行结果
- 6.非原创
服务器模型分为两种,循环服务器、并发服务器。
循环服务器: 服务器在同一时刻只能处理同一个客户端的请求。
并发服务器: 服务器在同一时刻能处理多个客户端的请求。
原理TCP服务器默认的就是一个循环服务器,因为内部有两个阻塞的函数, accept recv 会相互影响
UDP服务器默认的就是一个并发服务器,因为只有一个阻塞的函数 recvfrom
代码实现 服务器—01server.cTCP服务器有两个阻塞的函数,recv 和 accept ,相互之间会有影响,使用 io多路复用可是实现同时处理多个IO,也就是服务器能支持并发。
#include客户端—02client.c#include #include #include #include #include #include #include #include #define ERRLOG(errmsg) do { printf("%s--%s(%d):", __FILE__, __func__, __LINE__); perror(errmsg); exit(-1); } while (0) //创建套接字-填充服务器网络信息结构体-绑定-监听 int socket_bind_listen(const char *argv[]); int main(int argc, const char *argv[]) { //检测命令行参数个数 if (3 != argc) { printf("Usage : %s n", argv[0]); exit(-1); } //创建套接字-填充服务器网络信息结构体-绑定-监听 int sockfd = socket_bind_listen(argv); //创建文件描述符表 fd_set readfds;//母本 FD_ZERO(&readfds);//清空 //每次给select使用,因为select会擦除掉一部分 fd_set readfds_temp; //用来备份原集合的 FD_ZERO(&readfds_temp);//清空 //记录表中最大的文件描述符 int max_fd = 0; //将要监视的文件描述符放到表中 FD_SET(sockfd, &readfds); max_fd = (max_fd > sockfd ? max_fd : sockfd);//记录最大文件描述符 int ret,ret1= 0; int accept_fd; char buff[128] = {0}; int i; while (1) { // select 每次返回后 会将没有就绪的文件描述符擦除,所以 //每次调用都需要重置这个集合 readfds_temp = readfds; ret= select(max_fd + 1, &readfds_temp, NULL, NULL, NULL); if (ret == -1) ERRLOG("select error"); //说明有文件描述符准备就绪 // && ret > 0 是为了提高效率: //select返回几个就绪的,就只处理几个就绪的就行了 //对于其他没有就绪的文件描述符 无需判断处理 for (i = 0; i < max_fd + 1&& ret > 0; i++) { if (FD_ISSET(i, &readfds_temp))//存在 {//说明i就绪了 if (i == sockfd) { //阻塞等待客户端连接--一旦有客户端连接就会解除阻塞 accept_fd = accept(sockfd, NULL, NULL); if (-1 == accept_fd) ERRLOG("accept error"); printf("客户端 [%d] 连接了n", accept_fd); //将acceptfd也加入到要监视的集合中 FD_SET(accept_fd, &readfds); //记录最大文件描述符 max_fd = (max_fd > accept_fd ? max_fd : accept_fd); } else {//说明有客户端发来消息了 accept_fd = i; //接收客户端发来的数据 if (0 > (ret1 = recv(accept_fd, buff,sizeof(buff), 0))) { perror("recv error"); break; } else if (0 == ret1)//客户端CTRL+C { printf("客户端 [%d] 断开连接n",accept_fd); //将该客户端在集合中删除 FD_CLR(accept_fd, &readfds); //关闭该客户端的套接字 close(i); continue;//结束本层本次循环 } else { if (0 == strcmp(buff, "quit")) { printf("客户端 [%d] 退出了n",accept_fd); //将该客户端在集合中删除 FD_CLR(accept_fd, &readfds); //关闭该客户端的套节字 close(i); continue;//结束本层本次循环 } printf("客户端 [%d] 发来数据:[%s]n", i, buff); //组装回复给客户端的应答 strcat(buff, "---996"); //回复应答 if (0 > (ret1= send(accept_fd, buff,sizeof(buff), 0))) { perror("send error"); break; } } } ret--; } } } //关闭监听套接字 一般不关闭 close(sockfd); return 0; } //创建套接字-填充服务器网络信息结构体-绑定-监听 int socket_bind_listen(const char *argv[]) { // 1.创建套接字 //IPV4 //TCP int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (-1 == sockfd) ERRLOG("socket error"); // 2.填充服务器网络信息结构体 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr));//清空 server_addr.sin_family = AF_INET;// IPV4 //端口号 填 8888 9999 6789 ...都可以 // atoi字符串转换成整型数 //htons将无符号2字节整型 主机-->网络 server_addr.sin_port = htons(atoi(argv[2])); // ip地址 要么是当前Ubuntu主机的IP地址 或者 //如果本地测试的化 使用 127.0.0.1 也可以 //inet_addr字符串转换成32位的网络字节序二进制值 server_addr.sin_addr.s_addr = inet_addr(argv[1]); //结构体长度 socklen_t server_addr_len = sizeof(server_addr); // 3.将套接字和网络信息结构体绑定 if (-1 == bind(sockfd, (struct sockaddr *)&server_addr, server_addr_len)) ERRLOG("bind error"); //将套接字设置成被动监听状态 if (-1 == listen(sockfd, 10)) ERRLOG("listen error"); return sockfd; }
#include执行结果 6.非原创#include #include #include #include #include #include #include #include #define ERRLOG(errmsg) do{ printf("%s--%s(%d):", __FILE__, __func__, __LINE__); perror(errmsg); exit(-1); }while(0) int main(int argc, const char *argv[]) { //检测命令行参数个数 if(3 != argc){ printf("Usage : %s n", argv[0]); exit(-1); } //1.创建套接字 int sockfd = socket(AF_INET, SOCK_STREAM, 0); if(-1 == sockfd) ERRLOG("socket error"); //2.填充服务器网络信息结构体 struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; //端口号 填 8888 9999 6789 ...都可以 server_addr.sin_port = htons(atoi(argv[2])); //ip地址 要么是当前Ubuntu主机的IP地址 或者 //如果本地测试的化 使用 127.0.0.1 也可以 server_addr.sin_addr.s_addr = inet_addr(argv[1]); //结构体长度 socklen_t server_addr_len = sizeof(server_addr); //与服务器建立连接 if(-1 == connect(sockfd, (struct sockaddr *)&server_addr, server_addr_len)) ERRLOG("connect error"); printf("---连接服务器成功---n"); char buff[128] = {0}; while(1){ scanf("%s", buff); int ret = 0; if(0 > (ret = send(sockfd, buff,sizeof(buff), 0))) ERRLOG("send error"); if(0 == strcmp(buff, "quit")) break; if(0 > (ret = recv(sockfd, buff,sizeof(buff), 0))) ERRLOG("recv error"); printf("收到服务器回复:[%s]n", buff); } //关闭套接字 close(sockfd); return 0; }



