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

0819 使用IP多路复用实现TCP并发服务器

Part 1.实现聊天室功能

1.service.c

#include <myhead.h>#define SER_PORT 8888
#define SER_IP "192.168.109.31"
#define MAX_CLIENTS 100typedef struct clientmsg {int fd; // 客户端的文件描述符char username[20];struct sockaddr_in cin;struct clientmsg *next;
} *ClientNode;// 创建客户端节点
ClientNode create_client_node(int fd, const char *username, struct sockaddr_in cin) 
{ClientNode node = (ClientNode)malloc(sizeof(struct clientmsg));if (node == NULL) return NULL;node->fd = fd;strncpy(node->username, username, sizeof(node->username)-1);node->cin = cin;node->next = NULL;return node;
}// 添加客户端到链表
void add_client(ClientNode *head, ClientNode new_client) 
{if (*head == NULL){*head = new_client;} else {ClientNode temp = *head;while (temp->next != NULL) {temp = temp->next;}temp->next = new_client;}
}// 从链表中移除客户端
void remove_client(ClientNode *head, int fd) 
{if (*head == NULL) return;ClientNode current = *head;ClientNode prev = NULL;while (current != NULL) {if (current->fd == fd) {if (prev == NULL) {*head = current->next;} else {prev->next = current->next;}free(current);return;}prev = current;current = current->next;}
}// 广播消息给所有客户端(除了发送者)
void broadcast_message(ClientNode head, int sender_fd, const char *message) 
{ClientNode current = head;while (current != NULL) {if (current->fd != sender_fd){send(current->fd, message, strlen(message), 0);}current = current->next;}
}// 获取客户端用户名
const char* get_client_username(ClientNode head, int fd) 
{ClientNode current = head;while (current != NULL) {if (current->fd == fd) {return current->username;}current = current->next;}return "Unknown";
}int main(int argc, const char *argv[]) 
{// 创建服务器端套接字int sfd = socket(AF_INET, SOCK_STREAM, 0);if (sfd == -1)ERR_MSG("socket error");// 设置地址重用int opt = 1;setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));// 创建服务器端地址信息结构体并且绑定struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) == -1)ERR_MSG("bind error");// 开启监听if (listen(sfd, 128) == -1)ERR_MSG("listen error");printf("服务器启动成功,等待客户端连接...\n");// 客户端链表头指针ClientNode clients = NULL;// 创建文件描述符集合fd_set readfds, tempfds;FD_ZERO(&readfds);FD_SET(sfd, &readfds);  // 添加监听套接字FD_SET(0, &readfds);    // 添加标准输入int maxfd = sfd;while (1){tempfds = readfds;// 使用select监听文件描述符int res = select(maxfd + 1, &tempfds, NULL, NULL, NULL);if (res == -1){ERR_MSG("select error");}// 检查是否有新的连接请求if (FD_ISSET(sfd, &tempfds)){struct sockaddr_in cin;socklen_t addrlen = sizeof(cin);int new_fd = accept(sfd, (struct sockaddr*)&cin, &addrlen);if (new_fd == -1) {perror("accept error");continue;}// 接收客户端发送的用户名char username[20] = "";int username_len = recv(new_fd, username, sizeof(username)-1, 0);if (username_len <= 0) {close(new_fd);continue;}username[username_len] = '\0';// 创建客户端节点并添加到链表ClientNode new_client = create_client_node(new_fd, username, cin);if (new_client == NULL){close(new_fd);continue;}add_client(&clients, new_client);// 将新客户端的文件描述符添加到select监听集合FD_SET(new_fd, &readfds);if (new_fd > maxfd){maxfd = new_fd;}printf("%s [%s:%d] 加入聊天室\n", username, inet_ntoa(cin.sin_addr), ntohs(cin.sin_port));// 广播欢迎消息char welcome_msg[128];snprintf(welcome_msg, sizeof(welcome_msg), "--------- %s 加入聊天室----------", username);broadcast_message(clients, new_fd, welcome_msg);}// 检查服务器终端输入if (FD_ISSET(0, &tempfds)) {char buf[128];if (fgets(buf, sizeof(buf), stdin) == NULL) continue;buf[strcspn(buf, "\n")] = '\0'; // 移除换行符if (strlen(buf) > 0) {// 广播服务器消息char server_msg[150];snprintf(server_msg, sizeof(server_msg), "服务器: %s", buf);broadcast_message(clients, -1, server_msg);}}// 检查所有客户端是否有数据可读ClientNode current = clients;while (current != NULL) {int client_fd = current->fd;ClientNode next = current->next; // 保存下一个指针,因为当前节点可能被删除if (FD_ISSET(client_fd, &tempfds)){char rbuf[128] = "";int res = recv(client_fd, rbuf, sizeof(rbuf)-1, 0);if (res <= 0){// 客户端断开连接printf("%s 已退出聊天室\n", current->username);// 广播退出消息char quit_msg[128];snprintf(quit_msg, sizeof(quit_msg), "----------%s 已退出聊天室----------", current->username);broadcast_message(clients, client_fd, quit_msg);// 清理资源close(client_fd);FD_CLR(client_fd, &readfds);remove_client(&clients, client_fd);// 更新maxfdif (client_fd == maxfd) {maxfd = sfd;ClientNode temp = clients;while (temp != NULL) {if (temp->fd > maxfd) {maxfd = temp->fd;}temp = temp->next;}}} else {rbuf[res] = '\0';// 广播客户端消息char broadcast_msg[256];snprintf(broadcast_msg, sizeof(broadcast_msg), "%s: %s", current->username, rbuf);broadcast_message(clients, client_fd, broadcast_msg);printf("%s: %s\n", current->username, rbuf);}}current = next;}}close(sfd);return 0;
}

2.cilent.c

#include<myhead.h>#define SER_PORT 8888
#define SER_IP "192.168.109.31"int main(int argc, const char *argv[])
{int cfd = socket(AF_INET,SOCK_STREAM,0);if(-1 == cfd)ERR_MSG("socket error");struct sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(SER_PORT);sin.sin_addr.s_addr = inet_addr(SER_IP);if(-1 == connect(cfd,(struct sockaddr *)&sin,sizeof(sin)))   ERR_MSG("bind error");printf("请输入用户名>>>\n");char namebuf[20] = "";scanf(" %s",namebuf);send(cfd,namebuf,strlen(namebuf),0);pid_t pid = fork();while(1){if(pid > 0){while(1){char rbuf[128] = "";int ret = recv(cfd,rbuf,sizeof(rbuf),0);if(ret < 0)break;printf("%s\n",rbuf);}close(cfd);kill(pid,SIGKILL);wait(NULL);}else if(pid == 0){//与服务器端进行通信while(1){//从套接字中读取消息char wbuf[128] = "";  fgets(wbuf,sizeof(wbuf),stdin);wbuf[strlen(wbuf)-1] = 0;if(strcmp(wbuf,"quit") == 0)break;send(cfd,wbuf,strlen(wbuf),0);		           }close(cfd);exit(0);}	}return 0;
}

Part 2.牛客网刷题

http://www.xdnf.cn/news/18313.html

相关文章:

  • 反向代理实现服务器联网
  • Auto-CoT:大型语言模型的自动化思维链提示技术
  • 微服务-08.微服务拆分-拆分商品服务
  • 深度学习环境搭建Windows+ TensorFlow 2.6.0 GPU 版
  • 亚矩阵云手机智能定位:助力Snapchat矩阵账号的本地化内容运营穿透技术
  • Apache IoTDB(4):深度解析时序数据库 IoTDB 在Kubernetes 集群中的部署与实践指南
  • 连接远程服务器上的 jupyter notebook,解放本地电脑
  • VSCode 从安装到精通:下载安装与快捷键全指南
  • 11.第11章 开发环境优化
  • 【C语言强化训练16天】--从基础到进阶的蜕变之旅:Day7
  • Nacos-6--Naco的QUIC协议实现高可用的工作原理
  • 2025年- H98-Lc206--51.N皇后(回溯)--Java版
  • ARM架构下的cache transient allocation hint以及SMMUv2的TRANSIENTCFG配置详解
  • EasyExcel篇
  • OVS:ovn为什么默认选择Geneve作为二层隧道网络协议?
  • 【CV 目标检测】Fast RCNN模型③——模型训练/预测
  • c++最长上升子序列长度
  • 8.18网络编程——基于UDP的TFTP文件传输客户端
  • 力扣32:最长有效括号
  • 如何解决机器翻译的“幻觉“问题(Hallucination)?
  • 博客项目 Spring + Redis + Mysql
  • 深度研究系统、方法与应用的综述
  • android 实现表格效果
  • 接口文档——前后端分离开发模式下的“契约书“
  • Java原子类详解
  • MySQL的多版本并发控制(MVCC):
  • illustrator插件大全 免费插件介绍 Ai设计插件集合 (4)
  • LeetCode 每日一题 2025/8/11-2025/8/17
  • Windows 安装使用 MySQL
  • C++架构设计原则