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

多客户端 - 服务器结构-实操

实现2个客户端之间互相聊天
要求:
1、服务器使用 select 模型实现接受多个客户端连接,以及转发消息
2、客户端要求:使用 poll 模型解决 技能够 read 读取服务器发来的消息,又能够scanf读取键盘输入的信息
3、客户端服务器不允许开启额外线程和进程

服务器代码 (select模型)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/select.h>#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {if (argc != 2) {printf("Usage: %s <port>\n", argv[0]);return 1;}int server_fd, new_socket, client_sockets[MAX_CLIENTS];struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[BUFFER_SIZE] = {0};// 初始化客户端socket数组for (int i = 0; i < MAX_CLIENTS; i++) {client_sockets[i] = 0;}// 创建socketif ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}// 设置socket选项if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))) {perror("setsockopt");exit(EXIT_FAILURE);}address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(atoi(argv[1]));// 绑定socketif (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) {perror("bind failed");exit(EXIT_FAILURE);}// 监听if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}printf("Server started on port %d\n", atoi(argv[1]));fd_set readfds;int max_sd, activity;while (1) {FD_ZERO(&readfds);FD_SET(server_fd, &readfds);max_sd = server_fd;// 添加客户端socket到集合for (int i = 0; i < MAX_CLIENTS; i++) {if (client_sockets[i] > 0) {FD_SET(client_sockets[i], &readfds);}if (client_sockets[i] > max_sd) {max_sd = client_sockets[i];}}// 使用select等待活动activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);if ((activity < 0) && (errno != EINTR)) {perror("select error");}// 检查新连接if (FD_ISSET(server_fd, &readfds)) {if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}printf("New connection, socket fd: %d, ip: %s, port: %d\n",new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));// 添加新socket到数组for (int i = 0; i < MAX_CLIENTS; i++) {if (client_sockets[i] == 0) {client_sockets[i] = new_socket;printf("Adding to list of sockets as %d\n", i);break;}}}// 检查客户端数据for (int i = 0; i < MAX_CLIENTS; i++) {int sd = client_sockets[i];if (FD_ISSET(sd, &readfds)) {int valread = read(sd, buffer, BUFFER_SIZE);if (valread == 0) {// 客户端断开连接getpeername(sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);printf("Host disconnected, ip: %s, port: %d\n",inet_ntoa(address.sin_addr), ntohs(address.sin_port));close(sd);client_sockets[i] = 0;} else {// 转发消息给所有客户端buffer[valread] = '\0';printf("Forwarding message: %s\n", buffer);for (int j = 0; j < MAX_CLIENTS; j++) {if (client_sockets[j] > 0 && client_sockets[j] != sd) {send(client_sockets[j], buffer, strlen(buffer), 0);}}}}}}return 0;
}

客户端代码 (poll模型)

#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 <poll.h>#define BUFFER_SIZE 1024int main(int argc, char *argv[]) {if (argc != 3) {printf("Usage: %s <ip> <port>\n", argv[0]);return 1;}int sock = 0;struct sockaddr_in serv_addr;char buffer[BUFFER_SIZE] = {0};struct pollfd fds[2];// 创建socketif ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {perror("socket creation failed");return -1;}serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(atoi(argv[2]));// 转换IP地址if (inet_pton(AF_INET, argv[1], &serv_addr.sin_addr) <= 0) {perror("invalid address");return -1;}// 连接服务器if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {perror("connection failed");return -1;}printf("Connected to server\n");// 设置poll结构fds[0].fd = STDIN_FILENO;    // 标准输入fds[0].events = POLLIN;fds[1].fd = sock;            // socketfds[1].events = POLLIN;while (1) {int ret = poll(fds, 2, -1); // 无限等待if (ret == -1) {perror("poll error");break;}// 检查键盘输入if (fds[0].revents & POLLIN) {memset(buffer, 0, BUFFER_SIZE);if (fgets(buffer, BUFFER_SIZE, stdin) == NULL) {break;}// 发送消息到服务器send(sock, buffer, strlen(buffer), 0);}// 检查服务器消息if (fds[1].revents & POLLIN) {memset(buffer, 0, BUFFER_SIZE);int len = recv(sock, buffer, BUFFER_SIZE, 0);if (len <= 0) {printf("Server disconnected\n");break;}printf("Received: %s", buffer);}}close(sock);return 0;
}

http://www.lryc.cn/news/587978.html

相关文章:

  • 如何在服务器上运行一个github项目
  • Rsyslog介绍及运用
  • 面试150 二叉树中的最大路径和
  • 26-计组-多处理器
  • K8S的平台核心架构思想[面向抽象编程]
  • 自动驾驶数据仓库:时间片合并算法。
  • ether.js—6—contractFactory以部署ERC20代币标准为例子
  • 0201-solidity基础-区块链-web3
  • OneCode 3.0 VFS客户端驱动(SDK)技术解析:从架构到实战
  • 虚拟货币交易:游走在合法与犯罪的生死线
  • 排序树与无序树:数据结构中的有序性探秘
  • 【【异世界历险之数据结构世界(二叉树)】】
  • 交换类排序的C语言实现
  • 删除当前项目关联的远程仓库(remote)
  • C#结构体:值类型的设计艺术与实战指南
  • 基于ASP.NET+SQL Server实现(Web)排球赛事网站
  • iOS高级开发工程师面试——RunTime
  • JAVA面试宝典 - 《MyBatis 进阶:插件开发与二级缓存》
  • 多尺度频率辅助类 Mamba 线性注意力模块(MFM),融合频域和空域特征,提升多尺度、复杂场景下的目标检测能力
  • 华曦达港股IPO丨AI Home生态构建,开启智能家居新篇章
  • 《Librosa :一个专为音频信号处理和音乐分析设计的Python库》
  • ServBay Windows 1.3.0 更新!新增系统监控与 Nginx 配置升级
  • [spring6: Resource ResourceLoader]-加载资源
  • GPT-4和Claude哪个好
  • UML建模和设计模式——常考点整理
  • VScode链接服务器一直卡在下载vscode服务器,无法连接成功
  • 视频动态范围技术演进:从SDR到HDR的影像革命
  • 【Unity】MiniGame编辑器小游戏(十三)最强射手【Shooter】(下)
  • wpf 实现窗口点击关闭按钮时 ​​隐藏​​ 而不是真正关闭,并且只有当 ​​父窗口关闭时才真正退出​​ 、父子窗口顺序控制与资源安全释放​
  • 单向链表、双向链表、栈、队列复习(7.14)