当前位置: 首页 > 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.dtcms.com/a/278320.html

相关文章:

  • 史上最清楚!读者,写者问题(操作系统os)
  • 基于 Gitlab、Jenkins与Jenkins分布式、SonarQube 、Nexus 的 CiCd 全流程打造
  • SQL创建三个表
  • 从 JSON 到 Python 对象:一次通透的序列化与反序列化之旅
  • Dubbo高阶难题:异步转同步调用链上全局透传参数的丢失问题
  • Selenium动态网页爬虫编写与解释
  • 【微信小程序】
  • 当你在 Git 本地提交后,因权限不足无法推送到服务端,若想撤销本次提交,可以根据不同的需求选择合适的方法,下面为你介绍两种常见方式。
  • 清除 Android 手机 SIM 卡数据的4 种简单方法
  • 云手机常见问题解析:解决延迟、掉线等困扰
  • 云手机的多重用途:从游戏挂机到办公自动化
  • kafka的部署
  • 从零实现浏览器摄像头控制与视频录制:基于原生 JavaScript 的完整指南
  • 如何将数据从一部手机传输到另一部手机?
  • 马蹄集 BD202401补给
  • C#中如何阻止硬件休眠
  • Vue 低代码可视化表单设计器 FcDesigner v3.3 版本发布!表格布局升级+精细化权限控制
  • JDK1.8 ReentrantLock相关源码
  • 代数基本定理
  • 多模态数据处理新趋势:阿里云ODPS技术栈深度解析与未来展望
  • RabbitMQ中队列长度限制(Queue Length Limit)详解
  • LVS的集群技术和分布式
  • hive的相关的优化
  • 传统机器学习在信用卡交易预测中的卓越表现:从R²=-0.0075到1.0000的华丽转身
  • Android 性能优化:启动优化全解析
  • Android 16系统源码_窗口动画(一)窗口过渡动画层级图分析
  • USB读写自动化压力测试
  • Android编译系统——基础介绍(一)
  • 微软发布BioEmu模型
  • spring shell 基础使用