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

UDP -- 简易聊天室

目录

gitee(内有详细代码)

图解

MessageRoute.hpp

UdpClient.hpp

UdpServer.hpp

Main.hpp

运行结果(本地通信)

如何分开对话显示?


gitee(内有详细代码)

chat_room · zihuixie/Linux_Learning - 码云 - 开源中国icon-default.png?t=O83Ahttps://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&#xff08;内有详细代码&#xff09; 图解 MessageRoute.hpp UdpClient.hpp UdpServer.hpp Main.hpp 运行结果&#xff08;本地通信&#xff09; 如何分开对话显示&#xff1f; gitee&#xff08;内有详细代码&#xff09; chat_room zihuixie/Linux_Lear…...

NVIDIA在CES 2025上的三大亮点:AI芯片、机器人与自动驾驶、全新游戏显卡

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

【通俗理解】AI的两次寒冬:从感知机困局到深度学习前夜

AI的两次寒冬&#xff1a;从感知机困局到深度学习前夜 引用&#xff08;中英双语&#xff09; 中文&#xff1a; “第一次AI寒冬&#xff0c;是因为感知机局限性被揭示&#xff0c;让人们失去了对算法可行性的信心。” “第二次AI寒冬&#xff0c;则是因为专家系统的局限性和硬…...

transformer深度学习实战CCTSDB中国交通标志识别

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

JavaWeb开发(六)XML介绍

1. XML介绍 1.1. 什么是XML &#xff08;1&#xff09;XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种很像HTML的标记语言。   &#xff08;2&#xff09;XML 的设计宗旨是传输数据(目前主要是作为配置文件)&#xff0c;而不是显示数据。   &#xff08;3&a…...

使用pbootcms开发一个企业官网

V:llike620 pbootcms开源PHP建站系统 https://www.pbootcms.com/ 配置网站 域名解析后&#xff0c;网站绑定到程序根目录即可 例如&#xff1a;本地域名是dobot.test &#xff0c;那么也要同步本地的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 应用编程中最基础的知识&#xff0c;即文件 I/O&#xff08;Input、Outout…...

【信息系统项目管理师】高分论文:论信息系统项目的风险管理(人民医院的信息系统)

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 论文1、规划风险管理2、项目风险识别3、风险定性分析4、风险定量分析5、制定风险应对6、实施风险应对计划7、监督风险论文 2022年6月,我作为项目经理承担了XX县人民医院的信息系统建设,该项目总投资300万,其…...

UE播放声音

蓝图中有两个播放声音的函数 Play Sound 2D 和 Play Sound at Location Play Sound 2D没有声音距离衰减&#xff0c;一般用于界面ui Play Sound at Location 有声音距离衰减&#xff0c;一般用于枪声&#xff0c;场景声等&#xff0c;比较常用...

Docker Compose 启动 Harbor 并指定网络

1. 介绍 Harbor 是一个开源的企业级 Docker 镜像仓库&#xff0c;提供镜像存储、访问控制、安全扫描等功能。使用 Docker Compose 启动 Harbor 时&#xff0c;您可以指定一个自定义网络&#xff0c;以便管理容器之间的网络通信。在本示例中&#xff0c;我们将创建一个名为 har…...

WebSocket 实战案例:从设计到部署

在前六篇文章中,我们深入探讨了 WebSocket 的基础原理、服务端开发、客户端实现、安全实践、性能优化和测试调试。今天,让我们通过一个实战案例,看看如何将这些知识应用到实际项目中。我曾在一个大型在线教育平台中,通过 WebSocket 实现了实时互动课堂,支持了数万名师生的同时在…...

selenium合集

环境搭建步骤 安装selenium pip install selenium 安装浏览器 安装浏览器驱动 谷歌浏览器&#xff1a;chromdriver.exe ie浏览器:ieserverdriver.exe FireFox浏览器:geckodriver.exe 特别注意⚠️&#xff1a;下载驱动版本必须与浏览器版本一致 下载地址 淘宝镜像&#xff1…...

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 主从数据库之间的数据同步 在实际项目中&#xff0c;为了提高 系统的读性能 和 数据的可用性&#xff0c;通常会使用 主从数据库架构。Spring Boot 提供了对 多数据源 的良好支持&#xff0c;可以轻松配置 主从数据库 的数据同步&#xff0c;实现…...

【小程序开发】- 小程序版本迭代指南(版本发布教程)

一&#xff0c;版本号 版本号是小程序版本的标识&#xff0c;通常由一系列数字组成&#xff0c;如 1.0.0、1.1.0 等。版本号的格式通常是 主版本号.次版本号.修订号 主版本号&#xff1a;当小程序有重大更新或不兼容的更改时&#xff0c;主版本号会增加。 次版本号&#xff1a…...

MySQL 间隙锁避免“可重复读”出现“幻读”

在数据库事务处理中&#xff0c;可重复读&#xff08;Repeatable Read&#xff09;是一个常用的隔离级别&#xff0c;但其默认行为可能导致幻读现象。然而&#xff0c;在 MySQL 的实现中&#xff0c;通过 **间隙锁&#xff08;Gap Lock&#xff09;**机制&#xff0c;能够避免幻…...

揭秘区块链隐私黑科技:零知识证明如何改变未来

文章目录 1. 引言&#xff1a;什么是零知识证明&#xff1f;2. 零知识证明的核心概念与三大属性2.1 完备性&#xff08;Completeness&#xff09;2.2 可靠性&#xff08;Soundness&#xff09;2.3 零知识性&#xff08;Zero-Knowledge&#xff09; 3. 零知识证明的工作原理4. 零…...

JavaWeb开发:从入门到精通

近年来&#xff0c;JavaWeb开发已经成为了互联网开发领域的重要技术之一。无论是大型企业还是个人项目&#xff0c;都离不开JavaWeb开发。本文将为您介绍JavaWeb开发的基本概念、常用技术和开发流程&#xff0c;帮助您快速入门并掌握这一技术。 一、JavaWeb开发的基本概念 Jav…...

2025年01月07日Github流行趋势

项目名称&#xff1a;khoj 项目地址url&#xff1a;https://github.com/khoj-ai/khoj项目语言&#xff1a;Python历史star数&#xff1a;20105今日star数&#xff1a;363项目维护者&#xff1a;debanjum, sabaimran, MythicalCow, aam-at, shantanuSakpal项目简介&#xff1a;你…...

c#集成npoi根据excel模板导出excel

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

【网络安全产品大调研系列】2. 体验漏洞扫描

前言 2023 年漏洞扫描服务市场规模预计为 3.06&#xff08;十亿美元&#xff09;。漏洞扫描服务市场行业预计将从 2024 年的 3.48&#xff08;十亿美元&#xff09;增长到 2032 年的 9.54&#xff08;十亿美元&#xff09;。预测期内漏洞扫描服务市场 CAGR&#xff08;增长率&…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)

目录 1.TCP的连接管理机制&#xff08;1&#xff09;三次握手①握手过程②对握手过程的理解 &#xff08;2&#xff09;四次挥手&#xff08;3&#xff09;握手和挥手的触发&#xff08;4&#xff09;状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

python如何将word的doc另存为docx

将 DOCX 文件另存为 DOCX 格式&#xff08;Python 实现&#xff09; 在 Python 中&#xff0c;你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是&#xff0c;.doc 是旧的 Word 格式&#xff0c;而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...

面试高频问题

文章目录 &#x1f680; 消息队列核心技术揭秘&#xff1a;从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"&#xff1f;性能背后的秘密1.1 顺序写入与零拷贝&#xff1a;性能的双引擎1.2 分区并行&#xff1a;数据的"八车道高速公路"1.3 页缓存与批量处理…...

项目进度管理软件是什么?项目进度管理软件有哪些核心功能?

无论是建筑施工、软件开发&#xff0c;还是市场营销活动&#xff0c;项目往往涉及多个团队、大量资源和严格的时间表。如果没有一个系统化的工具来跟踪和管理这些元素&#xff0c;项目很容易陷入混乱&#xff0c;导致进度延误、成本超支&#xff0c;甚至失败。 项目进度管理软…...

iOS 项目怎么构建稳定性保障机制?一次系统性防错经验分享(含 KeyMob 工具应用)

崩溃、内存飙升、后台任务未释放、页面卡顿、日志丢失——稳定性问题&#xff0c;不一定会立刻崩&#xff0c;但一旦积累&#xff0c;就是“上线后救不回来的代价”。 稳定性保障不是某个工具的功能&#xff0c;而是一套贯穿开发、测试、上线全流程的“观测分析防范”机制。 …...

uni-app学习笔记二十七--设置底部菜单TabBar的样式

官方文档地址&#xff1a;uni.setTabBarItem(OBJECT) | uni-app官网 uni.setTabBarItem(OBJECT) 动态设置 tabBar 某一项的内容&#xff0c;通常写在项目的App.vue的onLaunch方法中&#xff0c;用于项目启动时立即执行 重要参数&#xff1a; indexnumber是tabBar 的哪一项&…...

Redis——Cluster配置

目录 分片 一、分片的本质与核心价值 二、分片实现方案对比 三、分片算法详解 1. ‌范围分片&#xff08;顺序分片&#xff09;‌ 2. ‌哈希分片‌ 3. ‌虚拟槽分片&#xff08;Redis Cluster 方案&#xff09;‌ 四、Redis Cluster 分片实践要点 五、经典问题解析 C…...