欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > 网络编程Day9_IO多路复用 20240821

网络编程Day9_IO多路复用 20240821

2025/4/19 15:05:57 来源:https://blog.csdn.net/onion0857/article/details/141402591  浏览:    关键词:网络编程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;
}

运行效果

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词