网络编程Day9_IO多路复用 20240821

server/2024/10/18 21:24:10/

运行1个服务器和2个客户端实现效果:

服务器和2个客户端互相聊天,服务器和客户端都需要使用select模型去实现

服务器要监视2个客户端是否连接,2个客户端是否发来消息以及服务器自己的标准输入流

客户端要监视服务器是否发来消息以及客户端自己的标准输入流

在不开线程的情况下,实现互相聊天

select_server.c

#include <myhead.h>typedef struct Msg
{int fd;char text[128];
} Msg;// 将新连接生成套接字描述符添加进客户端套接字描述符数组
void insert_client(int *client_arr, int *client_len, int newfd)
{client_arr[*client_len] = newfd; // 将新描述符添加到数组(*client_len)++;                 // 增加客户端数量
}// 查找客户端描述符位置下标
int find_client(int *client_arr, int client_len, int newfd)
{for (int i = 0; i < client_len; i++){if (client_arr[i] == newfd) // 如果找到对应的描述符{return i; // 返回下标}}return -1; // 未找到,返回-1
}void remove_client(int *client_arr, int *client_len, int newfd)
{int tar = find_client(client_arr, *client_len, newfd); // 查找描述符位置if (tar == -1)                                         // 如果未找到{return; // 直接返回}int i = -1;for (i = tar; i < *client_len - 1; i++){client_arr[i] = client_arr[i + 1]; // 移动数组元素}client_arr[i] = 0; // 清空最后一个元素(*client_len)--;   // 减少客户端数量
}int main(int argc, const char *argv[])
{if (argc != 3) // 检查参数数量{printf("请输入正确的IP地址和端口号\n");return -1; // 参数错误,返回-1}// 1、创建套接字,设置端口号快速重启int sfd = socket(AF_INET, SOCK_STREAM, 0); // 创建套接字if (sfd == -1)                             // 检查套接字创建是否成功{perror("socket error");return -1; // 创建失败,返回-1}int opt = -1;if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) // 设置套接字选项{perror("setsockopt error");return -1; // 设置失败,返回-1}// 2、初始化地址信息并绑定struct sockaddr_in sin;                                    // 定义地址结构sin.sin_family = AF_INET;                                  // IPv4sin.sin_addr.s_addr = inet_addr(argv[1]);                  // 设置IP地址sin.sin_port = htons(atoi(argv[2]));                       // 设置端口号if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1) // 绑定套接字{perror("bind error");return -1; // 绑定失败,返回-1}// 3.监听端口连接if (listen(sfd, 128) == -1) // 开始监听{perror("listen error");return -1; // 监听失败,返回-1}// 4.接收连接请求,创建连接fd_set readfds;        // 定义文件描述符集合FD_ZERO(&readfds);     // 清空集合FD_SET(sfd, &readfds); // 将监听套接字添加到集合FD_SET(STDIN_FILENO, &readfds);int client_arr[128] = {0}; // 客户端描述符数组int client_len = 0;        // 当前客户端数量Msg msg = {0};while (1) // 主循环{fd_set temp = readfds;              // 复制集合select(FD_SETSIZE, &temp, 0, 0, 0); // 监视文件描述符if (FD_ISSET(sfd, &temp))           // 如果有新连接{bzero(&msg, sizeof(msg));struct sockaddr_in cin;                                     // 定义客户端地址结构socklen_t cin_len = sizeof(cin);                            // 地址结构大小int newfd = accept(sfd, (struct sockaddr *)&cin, &cin_len); // 接受连接if (newfd == -1)                                            // 检查连接是否成功{perror("accept error");return -1; // 接受失败,返回-1}printf("新客户端[%d]连接\n", newfd); // 输出连接信息msg.fd = newfd;strcpy(msg.text, "已上线");for (int j = 0; j < client_len; j++){int cfd = client_arr[j];if (cfd != newfd && cfd != sfd){send(cfd, &msg, sizeof(msg), 0);}}FD_SET(newfd, &readfds);                       // 将新描述符添加到集合insert_client(client_arr, &client_len, newfd); // 添加到客户端数组}else // 处理已连接的客户端{for (int i = 0; i < client_len; i++){int newfd = client_arr[i];  // 获取客户端描述符if (FD_ISSET(newfd, &temp)) // 如果有数据可读{char buf[128] = "";                      // 缓冲区int res = read(newfd, buf, sizeof(buf)); // 读取数据if (res == 0)                            // 如果客户端断开连接{msg.fd = newfd;strcpy(msg.text, "已下线");printf("客户端[%d]断开连接\n", msg.fd);for (int j = 0; j < client_len; j++){int cfd = client_arr[j];if (cfd != newfd && cfd != sfd){send(cfd, &msg, sizeof(msg), 0);}}FD_CLR(newfd, &readfds);                       // 从集合中移除remove_client(client_arr, &client_len, newfd); // 从数组中移除close(newfd); // 关闭描述符break;        // 退出当前循环}printf("客户端[%d]发来消息:%s\n", newfd, buf); // 输出客户端消息msg.fd = newfd;strcpy(msg.text, buf);for (int j = 0; j < client_len; j++){int cfd = client_arr[j];if (cfd != newfd && cfd != sfd){send(cfd, &msg, sizeof(msg), 0);}}}}}if (FD_ISSET(STDIN_FILENO, &temp)){bzero(&msg, sizeof(msg));msg.fd = 0;scanf("%s", msg.text);while (getchar() != 10);// 将数据发送给客户端for (int j = 0; j < client_len; j++){int cfd = client_arr[j];if (cfd != sfd){send(cfd, &msg, sizeof(msg), 0);}}}}return 0; // 程序结束
}

select_client.c

#include <myhead.h>
#define SER_PORT 6666		   // 服务器端口号
#define SER_IP "192.168.0.222" // 服务器IP地址typedef struct Msg
{int fd;char text[128];
} Msg;int main(int argc, const char *argv[])
{if (argc != 3){printf("请输入正确的IP地址和端口号\n");return -1;}// 1、创建用于通信的套接字文件描述符int cfd = socket(AF_INET, SOCK_STREAM, 0);if (cfd == -1){perror("socket error");return -1;}printf("cfd = %d\n", cfd);// 将端口号快速重用int reuse = 1;if (setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) == -1){perror("setsockopt error");return -1;}printf("端口号快速重用成功\n");// 2、绑定IP地址和端口号// 2.1 填充地址信息结构体struct sockaddr_in cin;cin.sin_family = AF_INET;cin.sin_addr.s_addr = inet_addr(argv[1]);cin.sin_port = htons(atoi(argv[2]));// 2.2 绑定工作if (bind(cfd, (struct sockaddr *)&cin, sizeof(cin)) == -1){perror("bind error");return -1;}printf("bind success\n");// 3、连接到服务器// 3.1 填充服务器地址信息结构体struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);// 3.2 连接服务器if (connect(cfd, (struct sockaddr *)&sin, sizeof(sin)) == -1){perror("connect error");return -1;}printf("服务器连接成功\n");// 4、数据收发char buf[128] = "";fd_set readfds;					// 定义文件描述符集合FD_ZERO(&readfds);				// 清空集合FD_SET(cfd, &readfds);			// 将监听套接字添加到集合FD_SET(STDIN_FILENO, &readfds); // 将标准输入添加到集合Msg msg = {0};while (1){bzero(buf, sizeof(buf));bzero(&msg, sizeof(msg));fd_set temp = readfds;				// 复制集合select(FD_SETSIZE, &temp, 0, 0, 0); // 监视文件描述符if (FD_ISSET(cfd, &temp)){int res = recv(cfd, &msg, sizeof(msg), 0);if (res == -1){perror("revc error");}else if(res == 0){printf("服务器已下线\n");break;}if (msg.fd == 0){printf("服务器发来消息: %s\n",msg.text);}else{printf("客户端[%d]发来消息: %s\n", msg.fd, msg.text);}}if (FD_ISSET(STDIN_FILENO, &temp)){bzero(buf, sizeof(buf));fgets(buf, sizeof(buf), stdin);buf[strlen(buf) - 1] = 0;if (strcmp(buf, "quit") == 0){printf("退出成功\n");break;}// 将数据发送给服务器send(cfd, buf, strlen(buf), 0);}}// 5、关闭套接字close(cfd);return 0;
}

运行效果


http://www.ppmy.cn/server/104398.html

相关文章

[mongodb][查询]MongoDb 模糊查询

模糊查询 {name:/HLCSOU17649/i}时间查询 {date:{"$gte":ISODate("2019-11-27T00:00:00Z"),"$lte":ISODate("2019-11-28T00:00:00Z")}}组合查询 {date{"$gte":ISODate("2019-11-27T00:00:00Z"),"$lte":…

Android 安卓Compose软键盘和Activity页面的协调处理问题

文章目录 问题展示1、输入框展示不完整&#xff0c;且布局被顶出屏幕外2、输入框被软键盘完全覆盖 解决方案最终演示 问题展示 1、输入框展示不完整&#xff0c;且布局被顶出屏幕外 这是默认处理的样式 2、输入框被软键盘完全覆盖 当在AndroidManifest.xml Activity标签上加…

Maven 管理依赖的详细步骤

1. 基本依赖管理&#xff1a; a. 在项目根目录创建 pom.xml 文件&#xff08;如果不存在&#xff09; b. 在 pom.xml 的 标签内添加依赖&#xff1a; <project><!-- 其他配置 --><dependencies><dependency><groupId>org.example</groupId&…

【标准知识】航天产品设计文件编号

按照QJ 1714-2011《航天产品设计文件管理制度》&#xff0c;梳理一下设计文件的编号要求。 01 设计文件 按照QJ 1714的规定&#xff0c;设计文件是由设计部门编制的&#xff0c;用以规定产品的组成、型式、结构尺寸、技术要求、原理以及制造、调试、试验、验收、使用、维护、…

SpringCloud整合Nacos配置中心

版本说明 spring-cloud version &#xff1a;2021.0.5.0 spring-boot.version&#xff1a;2.6.13 1. 引入依赖 <!--Nacos config--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config<…

XShell给Ubuntu虚拟机安装MySQL

准备工作&#xff1a;已经装好了虚拟机&#xff0c;并且已经下载了Linux系统&#xff0c;如 Linux Mint &#xff0c; Ubuntu 等&#xff0c;今天以目前较为流行的Ubuntu为示例 1.为什么选择Linux&#xff1f; 在现代软件开发中&#xff0c;Linux系统已成为开发和部署的首选平…

Mysql 集群技术

一、Mysql 在服务器中的部署方法 在企业中90%的服务器操作系统均为Linux在企业中对于Mysql的安装通常用源码编译的方式来进行官网&#xff1a;http://www.mysql.com 1.1 在Linux下部署mysql cmake \ -DCMAKE_INSTALL_PREFIX/usr/local/mysql \ #指定安装路径 -DMYSQL_DATADI…

H7-TOOL脱机烧录的UID加密操作方法,支持一键生成目标板C代码,方便大家轻松操作(2024-08-20,已发布)

UID加密使用比较方便&#xff0c;对应的C代码模板已经做好&#xff0c;使用TOOL上位机生成后&#xff0c;直接复制粘贴到自己的工程即可使用。返回1表示解密成功&#xff0c;返回0表示失败。 【UID加密原理】 1、烧录器在烧录芯片时&#xff0c;按照指定的算法将UID码编码为…