多人聊天室 (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.hqwc.cn/news/540923.html

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

相关文章

【Android】AOSP 架构

Android 官网对 AOSP 结构图进行了更新&#xff0c;如下所示&#xff1a; Android 应用&#xff08;Android Apps&#xff09; 完全使用 Android API 开发的应用。在某些情况下&#xff0c;设备制造商可能希望预安装 Android 应用以支持设备的核心功能。 特权应用&#xff08…

交换机/路由器的存储介质-思科

交换机/路由器的存储介质-思科 本文主要介绍网络设备的存储介质组成。 RAM(random-accessmemory&#xff0c;随机访问存储器) RAM中内容断电丢失&#xff0c;主要用于运行操作系统、运行配置文件、IP 路由表:、ARP 缓存、数据包缓存区。 ROM(read-only memory&#xff0c;只…

php7.3.4连接sqlserver(windows平台)

前言 有个项目需要手上laravel连接客户的sqlserver数据库读取数据&#xff0c;故在本地开发的lnmp环境中&#xff0c;php需要增加扩展 过程 从微软官网下载sqlsrv扩展,注意注意php版本&#xff0c;下载地址 解压的文件会有nts和ts两个版本&#xff0c;本地打开phpinfo查看 将…

Linux内核--基本概念/基本结构和组件

提示&#xff1a;本系列文章重点学习Linux内核 Linux内核--基本概念/基本结构和组件 简介一、基础概念1.六项工作内容2.根文件系统&#xff08;Root File System&#xff09;&#xff1a;3.交叉编译&#xff08;Cross-Compilation&#xff09;&#xff1a;4.设备树&#xff08;…

Oracle登录错误ERROR: ORA-01031: insufficient privileges解决办法

这个问题困扰了我三个星期&#xff0c;我在网上找的解决办法&#xff1a; 1.控制面板->管理工具->计算机管理->系统工具->本地用户和组->ORA_DBA组。 但我电脑上根本找不到。 2.在oracle安装目录下找到oradba.exe运行。 最开始我都不到这个oradba.exe文件在哪…

软件设计师:12 - 下午题历年真题

章节章节01-计算机组成原理与体系结构07 - 法律法规与标准化与多媒体基础02 - 操作系统基本原理08 - 设计模式03 - 数据库系统09 - 软件工程04 - 计算机网络10 - 面向对象05 - 数据结构与算法11 - 结构化开发与UML06 - 程序设计语言与语言处理程序基础12 - 下午题历年真题End -…

3 数据链路层(二):差错控制、差错检测和纠正

目录 1 差错控制、差错检测和纠正1.1 差错类型和差错控制1、差错类型2、差错控制方法 1.2 差错控制编码1、差错控制编码介绍2、差错控制方式3、编码分类4、几种差错控制编码的价绍奇偶校验码循环冗余校验码海明码 1 差错控制、差错检测和纠正 数据在传输中可能被破坏&#xff…

一文看懂红帽认证含金量有多高!

近期好多人来问红帽认证&#xff0c;有些是还在校的大学生&#xff0c;有些是已经工作的运维小伙伴。!现在的就业和职场环境下&#xff0c;系统学Linux确实是非常必要的。今天就给大家详细介绍下红帽认证&#xff0c;看看它的含金量有多高! 红帽认证是什么?红帽认证等级?红帽…

vmware workstation虚拟机报错”该虚拟机似乎正在使用中“

虚拟机报错&#xff1a; 解决方法&#xff1a; 进入到虚拟机的安装目录里&#xff0c;将lck结尾的文件删掉即可 重新点击虚拟机恢复正常

跨境热点!TikTok直播网络要求是什么?

TikTok直播作为一种互动性强、实时性要求高的社交媒体形式&#xff0c;对网络环境有着一系列特定的需求。了解并满足这些需求&#xff0c;对于确保用户体验、提高直播质量至关重要。本文将深入探讨TikTok直播对网络环境的要求以及如何优化网络设置以满足这些要求。 TikTok直播的…

Stripe Web 购买集成

图片被吞了可以来这里看&#xff1a;https://juejin.cn/post/7346388511338381364 1. 准备事项 Stripe 账号域名以及配套的网站Stripe 账号付款信息公钥和私钥 2. 配置产品以及价格 可以通过 API 或者 Stripe 管理后台来进行配置 产品&#xff1a;就是商品&#xff0c;只需…

黑群晖: 未在 DS918+ 中检测到硬盘 之 解决方案

黑群晖&#xff1a; 未在 DS918 中检测到硬盘 之 解决方案 操作如下&#xff1a; 进入BIOS&#xff0c;将sata operation 设置为 AHCI 即可