使用select端口复用技术,实现一个聊天程序的服务器端的C++代码

时间: 2024-05-14 17:19:46 浏览: 8
一、select 端口复用技术介绍 select 函数可以实现多路复用输入/输出,即一个进程可以同时监听多个文件句柄,以便在任意一个文件句柄上完成输入输出操作。 select 函数使用一个 fd_set 类型的数组来检测一组文件描述符的状态。fd_set 变量的具体定义如下: ``` typedef struct fd_set { u_int fds_bits[howmany(FD_SETSIZE, NFDBITS)]; } fd_set; ``` 其中 howmany 和 NFDBITS 定义在头文件 sys/param.h 中,其功能主要是做一些与文件描述符相关的计算。 select 函数的原型如下: ``` int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` 参数 nfds: 所有描述符的最大值加 1. 参数 readfds, writefds, exceptfds 分别指向可读、可写和异常事件的 fd_set 集合。 参数 timeout 为等待该文件描述符集合变化的最大超时时间。如果为 NULL,则函数会一直阻塞直到有描述符集合中的事件发生。 成功时,该函数会返回待处理事件的文件数量。如果在超时时间内没有检测到任何文件,则返回 0,如果函数发生错误,返回 -1。 二、聊天程序服务器端C代码实现 下面是一个基于 select 的服务器端聊天程序的 C 代码实现,其中包含了接收和转发客户端发送消息的功能: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/select.h> #include <sys/time.h> #define MAX_CLIENTS 5 #define BUFFER_SIZE 1024 int server_socket; void die(char *msg) { perror(msg); exit(EXIT_FAILURE); } int init_server(char *ip, int port) { int sockfd; struct sockaddr_in servaddr; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { die("socket"); } bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); if (ip == NULL) { servaddr.sin_addr.s_addr = htonl(INADDR_ANY); } else { if (inet_pton(AF_INET, ip, &servaddr.sin_addr) <= 0) { die("inet_pton"); } } if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) { die("bind"); } if (listen(sockfd, MAX_CLIENTS) < 0) { die("listen"); } return sockfd; } int main(int argc, char **argv) { struct sockaddr_in client_addr; int maxfd = -1, max_i = -1; char buffer[BUFFER_SIZE]; fd_set rset, all_set; int clients[MAX_CLIENTS]; int i, maxi = -1; for (i = 0; i < MAX_CLIENTS; i++) { clients[i] = -1; } server_socket = init_server(NULL, 12345); FD_ZERO(&all_set); FD_SET(server_socket, &all_set); maxfd = server_socket; while (1) { rset = all_set; int nready = select(maxfd + 1, &rset, NULL, NULL, NULL); if (nready < 0) { die("select"); } if (FD_ISSET(server_socket, &rset)) { socklen_t len = sizeof(client_addr); int connfd; if ((connfd = accept(server_socket, (struct sockaddr*) &client_addr, &len)) < 0) { die("accept"); } for (i = 0; i < MAX_CLIENTS; i++) { if (clients[i] < 0) { clients[i] = connfd; break; } } if (i == MAX_CLIENTS) { fprintf(stderr, "too many clients\n"); close(connfd); continue; } printf("New client connected from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); FD_SET(connfd, &all_set); if (connfd > maxfd) { maxfd = connfd; } if (i > maxi) { maxi = i; } if (--nready <=0) { continue; } } for (i = 0; i <= maxi; i++) { int sockfd = clients[i]; if (sockfd < 0) { continue; } if (FD_ISSET(sockfd, &rset)) { ssize_t n = recv(sockfd, buffer, BUFFER_SIZE-1, 0); if (n < 0) { die("recv"); } if (n == 0) { FD_CLR(sockfd, &all_set); close(sockfd); clients[i] = -1; } else { buffer[n] = '\0'; printf("Received message: %s", buffer); for (int j = 0; j <= maxi; j++) { int dest_sockfd = clients[j]; if (dest_sockfd < 0 || dest_sockfd == sockfd) { continue; } if (send(dest_sockfd, buffer, strlen(buffer), 0) < 0) { die("send"); } } } if (--nready <= 0) { break; } } } } return EXIT_SUCCESS; } ``` 该程序使用了 MAX_CLIENTS 个 TCP 连接,对于每个连接在缓冲区的最大长度为 BUFFER_SIZE。程序使用了一个循环来监听所有连接,如果有连接上来,程序将把它加入到待监听文件句柄集合中。程序每次循环都需要调用 select 函数来检测每个连接上事件的发生情况,接着程序根据事件类型来进行相应的处理。如果是有连接到来,程序就将该连接加入到待监听的文件句柄集合中,并记录该连接的描述符,在最大描述符的范围内更新相应的值;如果是有客户端发来消息,程序就遍历所有连接并将消息转发到其他客户端上。

相关推荐

最新推荐

recommend-type

微信小程序自定义select下拉选项框组件的实现代码

微信小程序中没有select下拉选项框,所以只有自定义。这篇文章主要介绍了微信小程序自定义select下拉选项框组件,需要的朋友可以参考下
recommend-type

微信小程序select下拉框实现

主要介绍了微信小程序select下拉框实现源码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
recommend-type

iview实现select tree树形下拉框的示例代码

主要介绍了iview实现select tree树形下拉框的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
recommend-type

使用Python实现一个简单的项目监控

在公司里做的一个接口系统,主要是对接第三方的系统接口,所以,这个系统里会和很多其他公司的项目交互。随之而来一个很蛋疼的问题,这么多公司的接口,不同公司接口的稳定性差别很大,访问量大的时候,有的不怎么行...
recommend-type

jQuery 获取和设置select下拉框的值实现代码

获取Select : 获取select 选中的 text : $(“#ddlRegType”).find(“option:selected”).text(); 获取select选中的 value: $(“#ddlRegType “).val(); 获取select选中的索引: $(“#ddlRegType “).get(0)....
recommend-type

zigbee-cluster-library-specification

最新的zigbee-cluster-library-specification说明文档。
recommend-type

管理建模和仿真的文件

管理Boualem Benatallah引用此版本:布阿利姆·贝纳塔拉。管理建模和仿真。约瑟夫-傅立叶大学-格勒诺布尔第一大学,1996年。法语。NNT:电话:00345357HAL ID:电话:00345357https://theses.hal.science/tel-003453572008年12月9日提交HAL是一个多学科的开放存取档案馆,用于存放和传播科学研究论文,无论它们是否被公开。论文可以来自法国或国外的教学和研究机构,也可以来自公共或私人研究中心。L’archive ouverte pluridisciplinaire
recommend-type

MATLAB柱状图在信号处理中的应用:可视化信号特征和频谱分析

![matlab画柱状图](https://img-blog.csdnimg.cn/3f32348f1c9c4481a6f5931993732f97.png) # 1. MATLAB柱状图概述** MATLAB柱状图是一种图形化工具,用于可视化数据中不同类别或组的分布情况。它通过绘制垂直条形来表示每个类别或组中的数据值。柱状图在信号处理中广泛用于可视化信号特征和进行频谱分析。 柱状图的优点在于其简单易懂,能够直观地展示数据分布。在信号处理中,柱状图可以帮助工程师识别信号中的模式、趋势和异常情况,从而为信号分析和处理提供有价值的见解。 # 2. 柱状图在信号处理中的应用 柱状图在信号处理
recommend-type

HSV转为RGB的计算公式

HSV (Hue, Saturation, Value) 和 RGB (Red, Green, Blue) 是两种表示颜色的方式。下面是将 HSV 转换为 RGB 的计算公式: 1. 将 HSV 中的 S 和 V 值除以 100,得到范围在 0~1 之间的值。 2. 计算色相 H 在 RGB 中的值。如果 H 的范围在 0~60 或者 300~360 之间,则 R = V,G = (H/60)×V,B = 0。如果 H 的范围在 60~120 之间,则 R = ((120-H)/60)×V,G = V,B = 0。如果 H 的范围在 120~180 之间,则 R = 0,G = V,B =
recommend-type

JSBSim Reference Manual

JSBSim参考手册,其中包含JSBSim简介,JSBSim配置文件xml的编写语法,编程手册以及一些应用实例等。其中有部分内容还没有写完,估计有生之年很难看到完整版了,但是内容还是很有参考价值的。