当前位置: 首页 > news >正文

网络编程Day9_IO多路复用 20240821

运行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;
}

运行效果

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Python编码系列—Python WebSocket 实时通信:构建高效互动的网络应用
  • 什么是云原生?
  • HTTPS证书申请简明指南
  • redis能正常访问,但是springboot编译报错
  • Mobile-Agent项目部署与学习总结(DataWhale AI夏令营)
  • 前端宝典十四:Node缓存、安全与鉴权
  • FastAdmin 和 Dcat Admin从使用场景、适合人群、使用成本、资源完善程度、bug 解决、安全性全方位解析
  • linux网卡重命名命令
  • CentOS7 mysql-cluster安装与配置
  • Node.js 安装与使用及连接 MongoDB 的详细教程
  • uniapp重新编译在微信开发者工具跳转指定页面
  • 虹科方案 | 领航智能交通革新:虹科PEAK智行定位车控系统Demo版亮相
  • 如何使用 Nginx 解决跨域问题 (CORS)
  • 人工智能边缘计算应用教学解决方案
  • tomcat初步学习
  • Android Volley源码解析
  • node 版本过低
  • PHP CLI应用的调试原理
  • python大佬养成计划----difflib模块
  • spring boot 整合mybatis 无法输出sql的问题
  • underscore源码剖析之整体架构
  • 阿里云Kubernetes容器服务上体验Knative
  • 笨办法学C 练习34:动态数组
  • 分享几个不错的工具
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 前端攻城师
  • 如何将自己的网站分享到QQ空间,微信,微博等等
  • 我建了一个叫Hello World的项目
  • 掌握面试——弹出框的实现(一道题中包含布局/js设计模式)
  • 【云吞铺子】性能抖动剖析(二)
  • TPG领衔财团投资轻奢珠宝品牌APM Monaco
  • ​LeetCode解法汇总2808. 使循环数组所有元素相等的最少秒数
  • ​Python 3 新特性:类型注解
  • ​卜东波研究员:高观点下的少儿计算思维
  • # .NET Framework中使用命名管道进行进程间通信
  • #include到底该写在哪
  • (day18) leetcode 204.计数质数
  • (k8s)Kubernetes 从0到1容器编排之旅
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (编译到47%失败)to be deleted
  • (附源码)springboot家庭财务分析系统 毕业设计641323
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (剑指Offer)面试题34:丑数
  • (深度全面解析)ChatGPT的重大更新给创业者带来了哪些红利机会
  • (实测可用)(3)Git的使用——RT Thread Stdio添加的软件包,github与gitee冲突造成无法上传文件到gitee
  • **CI中自动类加载的用法总结
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .NET Core 通过 Ef Core 操作 Mysql
  • .NET 分布式技术比较
  • .NET精简框架的“无法找到资源程序集”异常释疑
  • /etc/sudoer文件配置简析
  • [Algorithm][动态规划][路径问题][不同路径][不同路径Ⅱ][珠宝的最高价值]详细讲解
  • [Angular] 笔记 6:ngStyle
  • [Cloud Networking] Layer3 (Continue)
  • [Day 44] 區塊鏈與人工智能的聯動應用:理論、技術與實踐