UDP -- 简易聊天室
目录
gitee(内有详细代码)
图解
MessageRoute.hpp
UdpClient.hpp
UdpServer.hpp
Main.hpp
运行结果(本地通信)
如何分开对话显示?
gitee(内有详细代码)
chat_room · zihuixie/Linux_Learning - 码云 - 开源中国https://gitee.com/zihuixie/linux_-learning/tree/master/chat_room
图解
MessageRoute.hpp
#pragma once
#include <iostream>
#include <vector>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/socket.h>
#include <functional>
#include "InetAddr.hpp"
#include "ThreadPool.hpp"
#include "LockGuard.hpp"// 通信模块:实现消息的转发
//只要有人发消息,就把该消息转发给所有的在线用户using task_t =std::function<void()>;class MessageRoute
{
private:// 判断地址是否已保存数组中bool IsExists(const InetAddr &addr){for (auto a : _online_user){if (a == addr)return true;}return false;}public:MessageRoute(){pthread_mutex_init(&_mutex,nullptr);}~MessageRoute(){pthread_mutex_destroy(&_mutex);}// 添加用户void Adduser(const InetAddr &addr){LockGuard lock(&_mutex);//保护数组临界资源// 已存在则不添加if (IsExists(addr))return;_online_user.push_back(addr);}// 删除用户void Deluser(const InetAddr &addr){LockGuard lock(&_mutex);//保护数组临界资源// 不存在该用户,不需要删除if (!IsExists(addr))return;// 用迭代器删除for (auto iter = _online_user.begin(); iter != _online_user.end(); iter++){if (*iter == addr){_online_user.erase(iter);break;}}}//转发消息void RouteHelper(int sockfd,std::string message,InetAddr who){LockGuard lock(&_mutex);for(auto user:_online_user){//设置要发送的消息std::string send_message ="\n["+who.Ip()+":"+std::to_string(who.Port())+"]# "+message+"\n";struct sockaddr_in clientaddr=user.Addr();::sendto(sockfd,send_message.c_str(),send_message.size(),0,(struct sockaddr*)&clientaddr,sizeof(clientaddr));}}//通信模块//哪个套接字,发了什么消息,谁发的void Route(int sockfd,std::string message,InetAddr who){Adduser(who);//该用户要退出,删除该用户if(message=="Q" || message=="QUIT")Deluser(who);task_t t=std::bind(&MessageRoute::RouteHelper,this,sockfd,message,who);//让线程池来转发ThreadPool<task_t>::GetInstance()->Enqueue(t);}
private:pthread_mutex_t _mutex;std::vector<InetAddr> _online_user; // 用数组存储在线用户(用地址代表用户)
};
UdpClient.hpp
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Thread.hpp"using namespace ThreadModule;void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " serverip serverport\n"<< std::endl;
}int InitClient(std::string &serverip, uint16_t serverport, struct sockaddr_in *server)
{int sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0){std::cerr << "socket error" << std::endl;return -1;}// 初始化地址memset(server, 0, sizeof(struct sockaddr_in));server->sin_family = AF_INET;server->sin_port = htons(serverport);server->sin_addr.s_addr = inet_addr(serverip.c_str());return sockfd;
}// 接收消息
void recvmessage(int sockfd, std::string name)
{while (true){// 发送方的地址struct sockaddr_in peer;socklen_t len = sizeof(peer);char buffer[1024];// 获取接收方的地址ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);if (n > 0){buffer[n] = 0;//向标准错误中输出(标准错误也是个文件描述符)fprintf(stderr, "[%s]%s\n", name.c_str(), buffer);}}
}void sendmessage(int sockfd, struct sockaddr_in &server, std::string name)
{std::string message;while (true){printf("%s | Enter# ", name.c_str());fflush(stdout);std::getline(std::cin, message);// 传接收方的地址sendto(sockfd, message.c_str(), sizeof(message), 0, (struct sockaddr *)&server, sizeof(server));}
}int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}//填充地址struct sockaddr_in serveraddr;std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);int sockfd = InitClient(serverip, serverport, &serveraddr);if (sockfd == -1){return 1;}//定义函数并绑定参数func_t r = std::bind(&recvmessage, sockfd, std::placeholders::_1);func_t s = std::bind(&sendmessage, sockfd, serveraddr, std::placeholders::_1);//创建两个线程:发消息 && 收消息//就可以同时实现收消息 && 发消息 -- 全双工//如果收消息 和 发消息 都由一个线程实现 -- 半双工Thread Recver(r, "recver");Thread Sender(s, "sender");//启动线程Recver.Start();Sender.Start();Recver.Join();Sender.Join();return 0;
}
UdpServer.hpp
#pragma once#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
#include "InetAddr.hpp"
#include "Log.hpp"enum
{SOCKET_ERROR = 1, // 套接字创建失败BIND_ERROR, // 绑定失败USAGE_ERROR // 用法错误
};const static int defaultfd = -1; // 默认描述符的值为-1// 接收任务
using hander_message_t = std::function<void(int sockfd, const std::string message, const InetAddr who)>;class UdpServer
{
public:// 外界需要传端口号UdpServer(uint16_t port, hander_message_t hander_message): _port(port), _sockfd(defaultfd), _isrunning(false), _hander_message(hander_message){}void InitServer(){// 创建套接字_sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (_sockfd < 0) // 创建失败{LOG(FATAL, "socket error, %s, %d\n", strerror(errno), errno);exit(SOCKET_ERROR);}LOG(INFO, "socket create success, sockfd: %d\n", _sockfd);// 填 sockaddr_instruct sockaddr_in local; // 在栈上开辟空间bzero(&local, sizeof(local)); // 将内存设置为全0local.sin_family = AF_INET; // 协议族(用哪个协议)local.sin_port = htons(_port); // 端口号,转为网络序列(大端)local.sin_addr.s_addr = INADDR_ANY; // IP地址(任意地址)int n = bind(_sockfd, (struct sockaddr *)&local, sizeof(local));// 绑定失败if (n < 0){LOG(FATAL, "bind error,%s,%d\n", strerror(errno), errno);exit(BIND_ERROR);}LOG(INFO, "socket bind success\n");}// 启动服务器,服务器只接收信息和做应答void Start(){_isrunning = true;while (true){char message[1024];struct sockaddr_in peer; // 发送方的地址socklen_t len = sizeof(peer);// 接收数据报,返回值为接收到的字节数ssize_t n = recvfrom(_sockfd, message, sizeof(message) - 1, 0, (struct sockaddr *)&peer, &len);if (n > 0){message[n] = 0;InetAddr addr(peer); // 发送方的IP和端口号LOG(DEBUG, "get message from [%s:%d]:%s\n", addr.Ip().c_str(), addr.Port(), message);//转发消息_hander_message(_sockfd,message,addr);}_isrunning = false;}}~UdpServer(){}private:int _sockfd;uint16_t _port;bool _isrunning;hander_message_t _hander_message;
};
Main.hpp
#include<iostream>
#include<memory>
#include"UdpServer.hpp"
#include"MessageRoute.hpp"void Usage(std::string proc)
{std::cout<<"Usage:\n\t"<<proc<<" local_port\n"<<std::endl;
}int main(int argc,char* argv[])
{if(argc!=2){Usage(argv[0]);exit(USAGE_ERROR);}EnableScreen();MessageRoute route;//创建一个实例uint16_t port=std::stoi(argv[1]);std::unique_ptr<UdpServer> usvr=std::make_unique<UdpServer>(port,std::bind(&MessageRoute::Route,&route,std::placeholders::_1,std::placeholders::_2,std::placeholders::_3));usvr->InitServer();usvr->Start();return 0;}
运行结果(本地通信)
如何分开对话显示?
在启动客户端时,打开两个对话,输入命令 ./udpclient 127.0.0.1 8080 2>/dev/pts/2
相关文章:

UDP -- 简易聊天室
目录 gitee(内有详细代码) 图解 MessageRoute.hpp UdpClient.hpp UdpServer.hpp Main.hpp 运行结果(本地通信) 如何分开对话显示? gitee(内有详细代码) chat_room zihuixie/Linux_Lear…...

NVIDIA在CES 2025上的三大亮点:AI芯片、机器人与自动驾驶、全新游戏显卡
每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…...

【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜
AI的两次寒冬:从感知机困局到深度学习前夜 引用(中英双语) 中文: “第一次AI寒冬,是因为感知机局限性被揭示,让人们失去了对算法可行性的信心。” “第二次AI寒冬,则是因为专家系统的局限性和硬…...

transformer深度学习实战CCTSDB中国交通标志识别
本文采用RT-DETR作为核心算法框架,结合PyQt5构建用户界面,使用Python3进行开发。RT-DETR以其高效的实时检测能力,在多个目标检测任务中展现出卓越性能。本研究针对CCTSDB交通标志数据集进行训练和优化,该数据集包含丰富的CCTSDB交…...

JavaWeb开发(六)XML介绍
1. XML介绍 1.1. 什么是XML (1)XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种很像HTML的标记语言。 (2)XML 的设计宗旨是传输数据(目前主要是作为配置文件),而不是显示数据。 (3&a…...
使用pbootcms开发一个企业官网
V:llike620 pbootcms开源PHP建站系统 https://www.pbootcms.com/ 配置网站 域名解析后,网站绑定到程序根目录即可 例如:本地域名是dobot.test ,那么也要同步本地的hosts是 127.0.0.1 dobot.test 需要配置下伪静态规则 location / {if (!-e $r…...

Linux C编程——文件IO基础
文件IO基础 一、简单的文件 IO 示例二、文件描述符三、open 打开文件1. 函数原型2. 文件权限3. 宏定义文件权限4. 函数使用实例 四、write 写文件五、read 读文件六、close 关闭文件七、Iseek 绍 Linux 应用编程中最基础的知识,即文件 I/O(Input、Outout…...
【信息系统项目管理师】高分论文:论信息系统项目的风险管理(人民医院的信息系统)
更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文1、规划风险管理2、项目风险识别3、风险定性分析4、风险定量分析5、制定风险应对6、实施风险应对计划7、监督风险论文 2022年6月,我作为项目经理承担了XX县人民医院的信息系统建设,该项目总投资300万,其…...

UE播放声音
蓝图中有两个播放声音的函数 Play Sound 2D 和 Play Sound at Location Play Sound 2D没有声音距离衰减,一般用于界面ui Play Sound at Location 有声音距离衰减,一般用于枪声,场景声等,比较常用...
Docker Compose 启动 Harbor 并指定网络
1. 介绍 Harbor 是一个开源的企业级 Docker 镜像仓库,提供镜像存储、访问控制、安全扫描等功能。使用 Docker Compose 启动 Harbor 时,您可以指定一个自定义网络,以便管理容器之间的网络通信。在本示例中,我们将创建一个名为 har…...
WebSocket 实战案例:从设计到部署
在前六篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发、客户端实现、安全实践、性能优化和测试调试。今天,让我们通过一个实战案例,看看如何将这些知识应用到实际项目中。我曾在一个大型在线教育平台中,通过 WebSocket 实现了实时互动课堂,支持了数万名师生的同时在…...
selenium合集
环境搭建步骤 安装selenium pip install selenium 安装浏览器 安装浏览器驱动 谷歌浏览器:chromdriver.exe ie浏览器:ieserverdriver.exe FireFox浏览器:geckodriver.exe 特别注意⚠️:下载驱动版本必须与浏览器版本一致 下载地址 淘宝镜像࿱…...
JVM生产环境常用参数配置及调优建议
一、生产常用参数配置 JAVA_OPTS"-server -Xms3000m -Xmx3000m -Xmn1500m -XX:UseG1GC -XX:ConcGCThreads8 -XX:PrintGCDetails -XX:PrintGCTimeStamps -Xloggc:./g1-gc.log -XX:MaxMetaspaceSize256m -XX:-UseGCOverheadLimit -XX:UseCompressedOops -XX:HeapDumpOnOu…...
Spring Boot 3 实现 MySQL 主从数据库之间的数据同步
✅ Spring Boot 3 实现 MySQL 主从数据库之间的数据同步 在实际项目中,为了提高 系统的读性能 和 数据的可用性,通常会使用 主从数据库架构。Spring Boot 提供了对 多数据源 的良好支持,可以轻松配置 主从数据库 的数据同步,实现…...

【小程序开发】- 小程序版本迭代指南(版本发布教程)
一,版本号 版本号是小程序版本的标识,通常由一系列数字组成,如 1.0.0、1.1.0 等。版本号的格式通常是 主版本号.次版本号.修订号 主版本号:当小程序有重大更新或不兼容的更改时,主版本号会增加。 次版本号:…...
MySQL 间隙锁避免“可重复读”出现“幻读”
在数据库事务处理中,可重复读(Repeatable Read)是一个常用的隔离级别,但其默认行为可能导致幻读现象。然而,在 MySQL 的实现中,通过 **间隙锁(Gap Lock)**机制,能够避免幻…...

揭秘区块链隐私黑科技:零知识证明如何改变未来
文章目录 1. 引言:什么是零知识证明?2. 零知识证明的核心概念与三大属性2.1 完备性(Completeness)2.2 可靠性(Soundness)2.3 零知识性(Zero-Knowledge) 3. 零知识证明的工作原理4. 零…...
JavaWeb开发:从入门到精通
近年来,JavaWeb开发已经成为了互联网开发领域的重要技术之一。无论是大型企业还是个人项目,都离不开JavaWeb开发。本文将为您介绍JavaWeb开发的基本概念、常用技术和开发流程,帮助您快速入门并掌握这一技术。 一、JavaWeb开发的基本概念 Jav…...

2025年01月07日Github流行趋势
项目名称:khoj 项目地址url:https://github.com/khoj-ai/khoj项目语言:Python历史star数:20105今日star数:363项目维护者:debanjum, sabaimran, MythicalCow, aam-at, shantanuSakpal项目简介:你…...

c#集成npoi根据excel模板导出excel
NuGet中安装npoi 创建excel模板,替换其中的内容生成新的excel文件。 例子中主要写了这四种情况: 1、替换单个单元格内容; 2、替换横向多个单元格; 3、替换表格; 4、单元格中插入图片; using System.IO; …...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积
1.题目介绍 给定一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O…...

高考志愿填报管理系统---开发介绍
高考志愿填报管理系统是一款专为教育机构、学校和教师设计的学生信息管理和志愿填报辅助平台。系统基于Django框架开发,采用现代化的Web技术,为教育工作者提供高效、安全、便捷的学生管理解决方案。 ## 📋 系统概述 ### 🎯 系统定…...

Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...