多人聊天室 (epoll - Linux网络编程)

文章目录

    • 零、效果展示
    • 一、服务器代码
    • 二、客户端代码
    • 三、知识点
      • 1.bind()
    • 四、改进方向
    • 五、跟练视频

零、效果展示

一个服务器作为中转站,多个客户端之间可以相互通信。至少需要启动两个客户端。

在这里插入图片描述


三个客户端互相通信
在这里插入图片描述


一、服务器代码

chatServer.cpp

#include <cstdio>
#include <iostream>
#include <string>
#include <sys/epoll.h>  //epoll的头文件
#include <sys/socket.h> //socket的头文件
#include <unistd.h>     //close()的头文件
#include <netinet/in.h> //包含结构体 sockaddr_in
#include <map>          //保存客户端信息
#include <arpa/inet.h>  //提供inet_ntoa函数
using namespace std;const int MAX_CONNECT = 5; //全局静态变量,允许的最大连接数struct Client{int sockfd; //socket file descriptor 套接字文件描述符 string username;
};int main(){//创建一个epoll实例int epld = epoll_create(1);if(epld < 0){perror("epoll create error");return -1;}//创建监听的socketint sockfd = socket(AF_INET, SOCK_STREAM, 0);if(sockfd < 0){ //若socket创建失败,则返回-1perror("socket error");return -1;}//绑定本地ip和端口struct sockaddr_in addr;  //结构体声明,头文件是<netinet/in.h>addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port  = htons(9999);int ret = bind(sockfd,(struct sockaddr*)&addr,sizeof(addr));if(ret < 0){printf("bind error\n");cout << "该端口号已被占用,请检查服务器是否已经启动。" << endl;return -1;}cout << "服务器中转站已启动,请加入客户端。" << endl;//监听客户端ret = listen(sockfd,1024);if(ret < 0){printf("listen error\n");return -1;}//将监听的socket加入epollstruct epoll_event ev;ev.events = EPOLLIN;ev.data.fd = sockfd;ret = epoll_ctl(epld,EPOLL_CTL_ADD,sockfd,&ev); //防御性编程,方便出bug时快速定位问题if(ret < 0){printf("epoll_ctl error\n");return -1;}//保存客户端信息map<int,Client> clients;int clientCount = 0; //添加一个客户端计数器//循环监听while(true){struct epoll_event evs[MAX_CONNECT];int n = epoll_wait(epld,evs,MAX_CONNECT,-1);if(n < 0){printf("epoll_wait error\n");break;}for(int i = 0; i < n; i ++){int fd = evs[i].data.fd;//如果是监听的fd收到消息,则表示有客户端进行连接了if(fd == sockfd){struct sockaddr_in client_addr;socklen_t client_addr_len = sizeof(client_addr);int client_sockfd = accept(sockfd, (struct sockaddr*) & client_addr, &client_addr_len);if(client_sockfd < 0){printf("accept error,连接出错\n");continue;}//将客户端的socket加入epollstruct epoll_event ev_client;ev_client.events = EPOLLIN; //检测客户端有没有消息过来ev_client.data.fd = client_sockfd;ret = epoll_ctl(epld, EPOLL_CTL_ADD,client_sockfd,&ev_client);if(ret < 0){printf("epoll_ctl error\n");break;} //iner_ntoa() 将客户端的IP地址从网络字节顺序转换为点分十进制字符串clientCount++; //有新的客户端加入时,增加计数器printf("客户端%d已连接: IP地址为 %s\n", clientCount, inet_ntoa(client_addr.sin_addr));//保存该客户端信息Client client;client.sockfd = client_sockfd;client.username = "";clients[client_sockfd] = client;}else{char buffer[1024];int n = read(fd, buffer, 1024);if(n < 0){break; //处理错误}else if(n == 0){//客户端断开连接close(fd);epoll_ctl(epld,EPOLL_CTL_DEL, fd ,0);clients.erase(fd);}else{ // n > 0string msg(buffer,n);//如果该客户端username为空,说明该消息是这个客户端的用户名if(clients[fd].username == ""){clients[fd].username = msg;}else{string name = clients[fd].username;//把消息发给其他所有客户端for(auto &c:clients){if(c.first != fd){string full_message = '[' + name + ']' + ':' + msg;write(c.first, full_message.c_str(), full_message.length());//write(c.first,('[' + name + ']' + ":" + msg).c_str(),msg.size() + name.size() + 4);}}}}}}}//关闭epoll实例close(epld);close(sockfd);return 0;
}

二、客户端代码

client.cpp

注意g++编译时要加 -pthread

#include <cstdio>
#include <iostream> 
#include <cstring>       //memset()的头文件
#include <sys/socket.h>  //socket(),connect()等函数的头文件
#include <netinet/in.h>  //sockaddr_in的头文件
#include <arpa/inet.h>   //inet_pton()函数的头文件
#include <unistd.h>      //close()函数的头文件
#include <pthread.h>     //pthread创建线程和管理线程的头文件
using namespace std;#define BUF_SIZE 1024
char szMsg[BUF_SIZE];//发送消息
void* SendMsg(void *arg){int sock = *((int*)arg);while(1){//scanf("%s",szMsg);fgets(szMsg,BUF_SIZE,stdin); //使用fgets代替scanfif(szMsg[strlen(szMsg) - 1] == '\n'){szMsg[strlen(szMsg)- 1] = '\0'; //去除换行符}if(!strcmp(szMsg,"QUIT\n") || !strcmp(szMsg,"quit\n")){close(sock);exit(0);}send(sock, szMsg, strlen(szMsg), 0);}return nullptr;
}//接收消息
void* RecvMsg(void * arg){int sock = *((int*)arg);char msg[BUF_SIZE];while(1){int len = recv(sock, msg, sizeof(msg)-1, 0);if(len == -1){cout << "系统挂了" << endl;return (void*)-1;}msg[len] = '\0';printf("%s\n",msg);}return nullptr;
}int main()
{//创建socketint hSock;hSock = socket(AF_INET, SOCK_STREAM, 0);if(hSock < 0){perror("socket creation failed");return -1;}//绑定端口sockaddr_in servAdr;memset(&servAdr, 0, sizeof(servAdr));servAdr.sin_family = AF_INET;servAdr.sin_port = htons(9999);if(inet_pton(AF_INET, "172.16.51.88", &servAdr.sin_addr) <= 0){perror("Invalid address");return -1;}//连接到服务器if(connect(hSock, (struct sockaddr*)&servAdr, sizeof(servAdr)) < 0){perror("连接服务器失败");cout << "请检查是否已启动服务器。" << endl;return -1;}else{printf("已连接到服务器,IP地址:%s,端口:%d\n", inet_ntoa(servAdr.sin_addr), ntohs(servAdr.sin_port));printf("欢迎来到私人聊天室,请输入你的聊天用户名:");}//创建线程pthread_t sendThread,recvThread;if(pthread_create(&sendThread, NULL, SendMsg, (void*)&hSock)){perror("创建发送消息线程失败");return -1;}if(pthread_create(&recvThread, NULL, RecvMsg, (void*)&hSock)){perror("创建接收消息线程失败");return -1;}//等待线程结束pthread_join(sendThread, NULL);pthread_join(recvThread, NULL);//关闭socketclose(hSock);return 0;
}

三、知识点

1.bind()

在这里插入图片描述

在这里插入图片描述


四、改进方向

1.做的Linux端,只能在相同的IP上启动几个客户端自己玩。
后续可以做成Windows的exe,买个云服务器,然后发给朋友,进行通信。


五、跟练视频

陈子青多人聊天室-C/C++ 多人聊天室开发-epoll模型的IO多路复用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/747851.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【unity与android的交互(一)】安卓打包相关的常见参数详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

【解读】Synopsys发布2024年开源安全和风险分析报告OSSRA

软件供应链管理中&#xff0c;许可证和安全合规性至关重要。开源组件和库可降低风险&#xff0c;但需了解许可证内容。Synopsys 2023年审计发现&#xff0c;超过一半的代码库存在许可证冲突。MIT许可证是最常用的宽松许可证&#xff0c;但也与其他许可证存在不兼容风险。点此获…

AI新工具(20240315) 用户通过点击图像的特定部分实现域区图像动画; 先进的机器人操作框架

✨ 1: Follow-Your-Click 用户通过点击图像的特定部分实现域区图像动画 Follow-Your-Click 是一个开放领域的区域性图像动画创作工具&#xff0c;它可以通过简短的提示语实现图像中特定区域的动画效果。这种功能允许用户通过点击图像的特定部分并输入简短的指令&#xff08;例…

ViT学习笔记

一、Embedding层 对于标准的Transformer 模块&#xff0c;要求输入的是 token (向量 ) 序列&#xff0c;即二维矩阵 [num_token, token_dim] 在代码实现中&#xff0c;直接通过一个卷积层来实现以ViT- B/16为例&#xff0c;使用卷积核大小为 16x16 &#xff0c; stride 为 16 …

【Streamlit学习笔记】实现包含多个sheet的excel文件下载

1、什么是Streamlit Streamlit是一个免费的开源框架&#xff0c;用于快速构建和共享漂亮的机器学习和数据科学Web应用程序&#xff0c;官网链接 Streamlit Streamlit API链接 API reference 实际项目中遇到的问题&#xff1a;包含多个sheet的excel文件下载&#xff0c;下面将给…

传输层的UDP协议

1. UDP协议报文格式 1.1 16位端口号 UDP协议报文中&#xff0c;端口号占2个字节&#xff0c;包括 源端口号 和 目的端口号。 1.2 16位UDP长度 UDP报文长度为2个字节 &#xff0c;即UDP数据报长度为0~65535&#xff0c;也就是64kb。 1.3 16位UDP检验和 数据在网络传输的…

k8s helm 删除 tiller

kuberneter 上面装了 helm 想卸载还并不是那么简单, 参考 stackoverflow 回复 kubectl get -n kube-system secrets,sa,clusterrolebinding -o name|grep tiller|xargs kubectl -n kube-system delete kubectl get all -n kube-system -l apphelm -o name|xargs kubectl dele…

DHCP在企业网的部署及安全防范

学习目标&#xff1a; 1. DHCP能够解决什么问题&#xff1f; 2. DHCP服务器如何部署&#xff1f; 3. 私接设备会带来什么问题以及如何防范&#xff1f; 给DHCP服务器配置地址&#xff1a; 地址池&#xff1a; DHCP有2种分配模式&#xff1a;全局分配和接口分配 DHCP enable

element el-table 表格限制多选个数

本次的功能的要求是&#xff1a; 原本的引入的elment 的表格&#xff0c;去除全选框&#xff0c;版本对比的按钮&#xff0c;需要在选择版本&#xff0c;并且版本个数为2的时候&#xff0c;可点击&#xff0c;进行版本对比操作每次选择版本的时候&#xff0c;目前已有两个选择…

酷轻松气囊按摩护膝全新上线,科技呵护膝部健康

在快节奏的现代生活中&#xff0c;膝部健康问题逐渐引起人们的重视。长时间的站立、行走或运动&#xff0c;都可能对膝部造成不同程度的压力和损伤。 特别是家里有老人一直被老寒腿、关节发凉疼痛困扰的&#xff0c;经常一遇到下雨天&#xff0c;膝盖就不舒服&#xff1b;尤其到…

鸿蒙-自定义组件的生命周期

目录 自定义组件的生命周期 1.aboutToAppear 2.aboutToDisappear 3.onPageShow 4.onPageHide 5.onBackPress 日志输出 1.显示页面 2.页面点击返回按钮 3.页面跳转 4.页面返回 自定义组件的生命周期 先来一段列子 import router from ohos.router Entry Component…

虚拟机VMware上 centos7 的网络配置

第一步&#xff1a;权限的切换 由普通用户切换到超级用户 用户名为&#xff1a;root 密码为&#xff1a;自己安装 linux 时第一次设置的密码 su -root超级用户的命令提示符是“#”&#xff0c;普通用户的命令提示符是“$”。当看到你的命令提示符为“$”时&#xff0c;证明切…

pytorch之诗词生成6--eval

先上代码&#xff1a; import tensorflow as tf from dataset import tokenizer import settings import utils# 加载训练好的模型 model tf.keras.models.load_model(r"E:\best_model.h5") # 随机生成一首诗 print(utils.generate_random_poetry(tokenizer, model)…

WRF模型教程(ububtu系统)-WPS(WRF Pre-Processing System)概述

一、WPS简介 WRF 预处理系统 (WRF Pre-Processing System&#xff0c;WPS) &#xff0c;集成了基于Fortran和C编写的程序&#xff0c;这些程序主要用于处理输入到real.exe的数据。WPS主要有三个程序和一些辅助程序。 二、各程序介绍 主要的程序为geogrid.exe、ungrib.exe、met…

paddle ocr识别文字

paddle使用 # pip install paddlepaddle2.5.2 -i https://mirror.baidu.com/pypi/simple # pip install paddleocr2.7.0.3 -i https://mirror.baidu.com/pypi/simplefrom paddleocr import PaddleOCR from PIL import Image import numpy as npimage Image.open(./2.png) ocr…

Spring Boot Actuator介绍

大家在yaml中经常见到的这个配置 management: endpoints: web: exposure: #该配置线上需要去掉&#xff0c;会有未授权访问漏洞 include: "*" 他就是Actuator&#xff01; 一、什么是 Actuator Spring Boot Actuator 模块提供了生产级别…

微信小程序--分享如何与ibeacon蓝牙信标建立联系

ibeacon蓝牙设备 iBeacon是苹果公司2013年9月发布的移动设备用OS&#xff08;iOS7&#xff09;上配备的新功能。其工作方式是&#xff0c;配备有 低功耗蓝牙&#xff08;BLE&#xff09;通信功能的设备使用BLE技术向周围发送自己特有的ID&#xff0c;接收到该ID的应用软件会根…

Laravel Class ‘Facade\Ignition\IgnitionServiceProvider‘ not found 解决

Laravel Class Facade\Ignition\IgnitionServiceProvider not found 问题解决 问题 在使用laravel 更新本地依赖环境时&#xff0c;出现报错&#xff0c;如下&#xff1a; 解决 这时候需要更新本地的composer&#xff0c;然后在更新本地依赖环境。 命令如下&#xff1a; co…

19113133262(微信同号)【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024)

【征稿进行时|见刊、检索快速稳定】2024年区块链、物联网与复合材料与国际学术会议 (ICBITC 2024) 大会主题: (主题包括但不限于, 更多主题请咨询会务组苏老师) 区块链&#xff1a; 区块链技术和系统 分布式一致性算法和协议 块链性能 信息储存系统 区块链可扩展性 区块…

微信小程序将高德地图转为腾讯地图的自行车路线规划

微信小程序后台首页开发设置 相关文档 腾讯后台 微信小程序接入JDK JDK腾讯地图文档 腾讯路线规划文档 核心代码 <map id"myMap" ref"myMap" style"width: 100%; height: calc(100vh - 80px)":latitude"latitude" :scale&qu…