【Linux】udp服务器实现大型网络聊天室

udp终结篇~

文章目录

  • 前言
  • 一、udp服务器实现大型网络聊天室
  • 总结


前言

根据上一篇文章中对于英汉互译和远程操作的两个小功能大家应该已经学会了,我们的目的是让大家可以深刻的理解udp服务器所需要的接口已经实现的简单步骤,下面我们开始实现网络聊天室。


一、udp服务器实现大型网络聊天室

首先我们的实现思想是,当客户端发消息给服务器,这个时候服务器会将这条消息发给所有在线的用户,这样的话每个用户就能看到别人的消息了,所以我们首先创建一个文件来写用户管理的代码,由于我们的实现只是为了增加对udp服务器的理解,所以我们不会特别详细,就比如应该先注册再用账号和密码登录然后图形化界面什么的我们就不搞了,有兴趣的可以自己下去手动添加,我们只是写出最主要的功能。

#include <iostream>
using namespace std;class User
{
public:User(const string& ip,const uint16_t &port):_ip(ip),_port(port){}~User(){}
private:string _ip;uint16_t _port;
};

首先每个用户都要有自己的端口号和IP,然后我们就暂时写一个构造和析构就好了,在这里大家就可以设计每个用户的昵称等信息了。然后我们还需要有一个管理用户的类:

class UserManager
{
public:UserManager(){}~UserManager(){}void addUser(const string &ip, const uint16_t port){}void delUser(const string &ip, const uint16_t &port){}private:unordered_map<string,User> users;
};

我们的目的是直接通过用户的IP和端口号组成一个ID来实现映射,也就是让map去实现用户的增加删除操作。下面我们完善每个接口的代码:

    void addUser(const string &ip, const uint16_t port){string id = ip + "-" + to_string(port);users.insert(make_pair(id,User(ip,port)));}void delUser(const string &ip, const uint16_t &port){string id = ip + "-" + to_string(port);users.erase(id);}

首先每个用户都有自己的IP和端口号,所以我们用一个字符串去拼接他们的ID和端口号,这样就形成了唯一的id,然后用map的插入,插入的用户对象我们直接用匿名对象构造即可。删除一个用户也是同样的套路,下面我们再写一个判断是否在线的接口:

    bool isOnline(const string& ip,const uint16_t& port){string id = ip + "-" + to_string(port);if (users.find(id)==users.end()){return false;}else {return true;}}

我们就通过这个用户是否在map中来判断这个用户是否在线即可。管理用户的部分我们搞定后,下面我们再重新写一个回调方法:

 然后我们开始设计消息路由的回调方法:

void routeMessage(int sockfd,string clientip,uint16_t clientport,string message)
{//首先用户要上线就需要先给服务器发送onlineif (message=="online"){onlineuser.addUser(clientip,clientport);}if (onlineuser.isOnline(clientip,clientport)){//进行消息的路由}else{//不在线就提示用户需要先上线string response;struct sockaddr_in client;socklen_t len = sizeof(client);bzero(&client,sizeof(client));client.sin_family = AF_INET;client.sin_port = htons(clientport);client.sin_addr.s_addr = inet_addr(clientip.c_str());response = "你还没有上线,先输入online上线";sendto(sockfd,response.c_str(),response.size(),0,(struct sockaddr*)&client,len);}
}

我们首先判断用户是否输入online,如果输入就添加到map中,如果不在线就告诉用户需要先在线,那么我们该如何实现消息的路由呢?因为我们的map中存放的都是已经在线的用户,所以我们直接在用户管理中写一个函数,这个函数的目的是遍历map然后给每个用户发消息,因为我们是知道用户的端口号和ip的,所以发消息的内容就非常简单了:

首先包含网络相关的头文件,然后进行消息广播接口的编写:

 void broadcastMessage(int sockfd,const string& message){for (auto& ur:users){struct sockaddr_in client;bzero(&client,sizeof(client));client.sin_family = AF_INET;client.sin_port = htons(ur.second.returnport());client.sin_addr.s_addr = inet_addr(ur.second.returnip().c_str());sendto(sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&client,sizeof(client));}}

 首先我们发送消息必须用到sendto接口,这个接口会用到文件描述符,所以我们除了需要一个广播的消息,还需要文件描述符,然后给每个在线的用户发消息,发消息要用到用户的ip和port,这两个参数是用户的私有成员变量,所以我们写两个接口拿到这两个参数:

 当然我们考虑到广播消息的时候其他用户还要看到这条消息是谁发的,所以我们应该加入发消息这个人的ip和port:

 void broadcastMessage(int sockfd,const string& clientip,const uint16_t& clientport, const string& message){for (auto& ur:users){struct sockaddr_in client;bzero(&client,sizeof(client));client.sin_family = AF_INET;client.sin_port = htons(ur.second.returnport());client.sin_addr.s_addr = inet_addr(ur.second.returnip().c_str());string s = clientip + "-" + to_string(clientport) + "# ";s+=message;sendto(sockfd,s.c_str(),s.size(),0,(struct sockaddr*)&client,sizeof(client));}}

 我们在回调方法中还应该加入用户下线的信息,一旦下线我们就将这个用户从map中移除,上面服务端的代码我们就写完了,下面开始修改客户端的代码:

 客户端的代码中我们只需要让每次客户输入前有#的标记,最后消息打印完换行即可(注意:上面代码中我们用recvfrom这个函数的时候将结构体进行了填充,实际上recvfrom这个函数并不需要填充结构体)。下面我们引入多线程的知识,让我们的消息变成有一个线程专门读消息,另一个线程专门发送消息,这样的话即使有人不想说话也依旧能看到其他用户的聊天内容,如果我们不加入多进程或多线程的概念,那么就会出现用户不说话就无法看到其他用户的消息:

 首先加入一个线程的变量,然后我们让这个读线程去执行客户端中读取服务端发送消息的过程,主线程去给服务端发消息:

static void *readMessage(void* args){int sockfd = *(static_cast<int*>(args));pthread_detach(pthread_self());while (true){char buffer[1024];struct sockaddr_in temp;socklen_t len = sizeof(temp);int n = recvfrom(sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&temp, &len);if (n >= 0){buffer[n] = 0;}cout << buffer << endl;}return nullptr;}void run(){pthread_create(&reader,nullptr,readMessage,(void*)&_sockfd);struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(_serverip.c_str());server.sin_port = htons(_serverport);string message;while (!_quit){cout<<"# ";char commandbuffer[1024];fgets(commandbuffer,sizeof(commandbuffer)-1,stdin);message = commandbuffer;sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&server,sizeof(server));}

创建线程后让reader去执行read方法,将文件描述符传过去,由于主线程是死循环的发送消息,所以我们就不让主线程去等待reader线程了,直接将reader线程分离即可。分离后read线程就执行接收服务器消息的代码,主线程执行向服务器发送消息的代码。

下面我们演示一下:

首先我们可以搞一个小玩法,先创建一个管道文件,然后让服务器启动起来,将所有服务器发给我们客户端的消息我们都重定向到管道文件中,然后用另一个窗口使用cat命名查看管道文件中的内容,这样的话就实现了一个窗口我们只发送消息,一个窗口只查看服务器给我们发的消息,群聊消息可以在服务器看到。但是因为我们客户端run函数中是cout打印,cout会向一号描述符去打印,这就导致打印的消息也被重定向到管道了,所以我们将客户端中的消息用cerr打印,cerr会打印到二号描述符,这样就不会被重定向到管道文件了:

 上面重定向有什么作用呢?,作用其实就是让我们单独接收服务器给我们发送的消息,这也是重定向的一种玩法。当我们运行起来后发现我们不能上线输入online不识别:

 原因是我们输入的时候会带有回车,所以我们直接将回车去掉就好了:

 去掉后我们重新运行:

我们发现这次可以正常的上线了,我们再试试下线:

 下线也没有问题。下面我们开启两个Xshell来测试一下:

先开启服务端:

 然后我们已经用另一个xshell发了消息,客户端收到了消息:

 现在我们让这个用户上线:

 这个时候我们自己还没有上线:

 然后让自己上线:

 可以看到是没有问题的,下面可以看一下多人一起聊天的成果:

以上就是我们udp服务器实现大型网络聊天室的全部内容了。

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

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

相关文章

STM32单片机语音识别台灯控制系统人检测亮度调节

实践制作DIY- GC00156-语音识别台灯控制系统 一、功能说明&#xff1a; 基于STM32单片机设计-语音识别台灯控制系统 二、功能说明&#xff1a; 电路&#xff1a;STM32F103C系列最小系统串口语音识别模块LED灯板1个红外传感器 1.任何时候没有人则关闭灯。有人可以自动打开灯。…

Python物联网开发-Python_Socket通信开发-Python与Tcp协议物联网设备通信-Socket客户端

一、前言 Python在物联网开发中的重要愈来愈重&#xff0c;因此&#xff0c;掌握Python语言与物联网设备之间的通信就显得尤为重要&#xff0c;可以通过编写Python程序实现获取物联网设备的传感器数值并可以更改物联网设备上的执行器状态。 二、程序实现 首先&#xff0c;当使…

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(基本语法 一)

初识ArkTS语言 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript&#xff08;简称TS&#xff09;生态基础上做了进一步扩展&#xff0c;继承了TS的所有特性&#xff0c;是TS的超集。因此&#xff0c;在学习ArkTS语言之前&#xff0c;建议开发者具备TS语…

Linux内核的任务:

硬件与软件之间的中间层&#xff1a;内核在技术层面上充当硬件和软件之间的中间层&#xff0c;负责将应用程序的请求传递给硬件&#xff0c;并处理硬件设备和组件的寻址和操作。 应用程序的接口&#xff1a;对于应用程序来说&#xff0c;内核是它们与硬件之间的接口。应用程序通…

抓 https 报文新方案 -Magisk+LSPosed,来试试吧

关于如何抓取Android端https报文&#xff0c;在之前一篇文章中有介绍可以通过VitualXposedJustTrustMe模块禁用SSL验证&#xff0c;这样可以抓取到https&#xff0c;还是有一些同学反馈以下的一些问题&#xff1a; App在低版本的Android上不兼容&#xff0c;需要用高版本的And…

uniapp 集成七牛云,上传图片

1 创建项目 我是可视化创建项目的 &#xff0c;cli创建的项目可以直接使用npm安装七牛云。 2 拷贝qiniuUploader.js到项目&#xff0c;下面的回复 放了qiniuUploader.js百度云链接。 3 在需要使用qiniuUploader的vue文件 引入。 4 相册选择照片&#xff0c;或者拍照后&#xff…

视频问答新增或修改视频问答

通过问答id新增或修改视频问答题目 新增或修改视频问答 图3&#xff1a;视频问答功能&#xff08;观看效果&#xff09; 图4&#xff1a;视频问答功能&#xff08;观看效果&#xff09; 图5&#xff1a;视频问答功能&#xff08;观看效果&#xff09; 单元测试 Testpublic voi…

zabbix 6.0 监控LNPM环境

这里的LNPM是指Linux&#xff0c;Nginx,php-fpm和Mysql.具体版本如下。 Linux : centos7.9Nginx: 1.22.1php-fpm:7.4Mysql: 8.0 一、centos7.9 编译安装Nginx 为了弄清楚Nginx各种配置&#xff0c;我们采用编译安装的形式部署Nginx。 1.下载安装包 首先下载Nginx软件包&am…

为什么媒体和创意工作者更喜欢 Splashtop 进行远程访问

在当今快速发展的数字环境中&#xff0c;可靠的远程访问已成为全球媒体和创意工作者不可或缺的工具。 远程访问让创意工作者不再受传统办公空间边界的限制&#xff0c;完全实现了随时随地办公。无论是实时剪辑、高分辨率渲染还是其他创意任务&#xff0c;创意工作者对高性能远…

ENSP实验四:搭建VPN(GRE,配置安全策略)

首先分析一下数据的流向&#xff1a; PC1->PC2 1、FW1&#xff1a;trust->dmz 【192.168.1.1->192.168.2.1 ICMP】 2、AR1->AR2&#xff1a;【202.1.1.1->202.1.3.1|GRE|192.168.1.1->192.168.2.1 icmp】 3、FW2&#xff1a; ①untrust->local …

kotlin中使用Room数据库(包含升降级崩溃处理)

目录 1.导入依赖库 2.数据实体类 3.数据访问对象 (DAO) 4.数据库类 5.调用DAO里面的“增、删、改、查”方法 6.数据库升降级处理 升级&#xff08;保存数据库历史数据&#xff09;&#xff1a; 升级&#xff08;不保存数据库历史数据&#xff09;&#xff1a; 降级&…

SpringCloud学习路线(1)—— 从头开始的微服务

一、服务架构的历史 现有的服务框架&#xff1a; 单体架构 概念&#xff1a; 将业务所有功能集中在一个项目中开发&#xff0c;打包部署优点&#xff1a; 架构简单&#xff0c;部署成本低缺陷&#xff1a; 耦合度高 分布式架构 概念&#xff1a; 根据业务功能对系统进行拆分&a…

了解 3DS MAX 3D摄像机跟踪设置:第 7 部分

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 1. 在SynthEyes中跟踪素材 步骤 1 打开SynthEyes软件。 打开合成之眼 步骤 2 在跟踪素材之前&#xff0c;您需要设置首选项。因为&#xff0c;你 稍后将在 3ds Max 中工作&#xff0c;必须根据 3ds Max…

【源码解析】Mybatis执行原理

Mybatis执行原理 1.获取SqlSessionFactory2.创建SqlSession3.创建Mapper、执行SQL MyBatis 是一款优秀的持久层框架&#xff0c;MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息&#xff0c;将接口和…

elasticsearch学习入门+实战

学习链接1 基础概念 官网学习&#xff1a;地址 基本命令 PS&#xff1a;使用Apifox测试 查询所有索引库 添加索引库 添加时&#xff0c;加入分词器 添加时&#xff0c;加入记录属性值 查询获取索引库 删除索引库 添加文档 必须要在添加文档值的时候用【_doc】&a…

如何在 Excel 中快速生成随机密码?

有时&#xff0c;我们可能想创建随机密码来保护某些重要内容。 但是&#xff0c;您有什么技巧可以在Excel中快速生成随机密码&#xff1f; 在这里&#xff0c;我有一些可以在Excel工作表中处理的方法。 用公式生成随机密码 使用插入随机数据生成随机密码​编辑 用公式生成随机…

IPD跟敏捷、DevOps一样吗?有什么区别?

1992年在激烈的全球市场竞争下&#xff0c;IBM遭遇到了严重的财政困难&#xff0c;公司销售收入停止增长&#xff0c;利润急剧下降。经过内部分析&#xff0c;IBM发现他们在研发费用、研发损失费用和产品上市时间等几个方面远远落后于业界最佳。为了重新获得市场竞争优势&#…

思维决定发展,测试人也不例外

最近特别懒&#xff0c;不想码字&#xff0c;原本写作就很差&#xff0c;更是退化严重。社招和校招面试过很多人&#xff0c;从十年前自己还很弱的时候学着面试&#xff0c;到数百次面试积累之后&#xff0c;面对候选人的时候&#xff0c;我的内心依然有些许紧张&#xff0c;非…

初识protobuf

Protobuf 全称Protocol Buffers&#xff08;协议缓冲区&#xff09;&#xff0c;是一种轻量级、高效的数据序列化格式&#xff0c;由Google开发。它被设计用于结构化数据的序列化、反序列化以及数据交换&#xff0c;常用于网络通信和数据存储等领域。 Protobuf使用简洁的消息描…

高效又安全的企业大数据传输解决方案推荐

在当前的商业领域中&#xff0c;企业大数据传输是一个重要而复杂的问题。随着企业规模和数据量的扩大&#xff0c;如何安全可靠、高效快速地传输大数据成为了许多企业需要面对的挑战。本文将介绍几种值得考虑的企业大数据传输解决方案&#xff0c;以帮助企业有效应对这一挑战。…