【Linux】基于UDP协议的“聊天室”

目录

预备知识

基本思路

服务端设计

重要接口详解

服务端核心代码

服务端运行代码

客户端设计


预备知识

UDP协议(User Datagram Protocal用户数据报协议)

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

基本思路

如下是我们设计的一个简单的“聊天室”的大致框架图:

        “聊天室”分为两个角色,一个是客户端,即参与聊天的用户,另一个是提供服务的服务端,负责接收来自客户端,对接收到的信息加工处理,显示发送方的ip和端口号,再转发给已经加入服务端所创建的用户列表中的所有用户(即已经在该聊天室的用户)。

服务端设计

重要接口详解

服务端设计只要有以下几个步骤:

//第一步   创建套接字socket

sockfd=socket (int domain, int type, int protocol) 

        1.domain指明使用的协议族,常用有AF_INET 、AF_INET6、AF_UNIX 、AF_ROUTE

        2. type指明socket类型 有三种:SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、  SOCK_RAW(原始类型,允许对底层协议如IP或ICMP进行直接访问,不太常用)

        3.protocol  通常赋值为0;

        --成功返回非负值的socket描述符,失败返回-1

//第二步   将创建的socket绑定到指定的IP地址和端口上

bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen)

        --成功返回0,失败返回1

PS:

        1.uint16_t需要头文件 #include <unistd.h>

        2.sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义

        3.bzero函数头文件是string.h(C语言)或 cstring(C++)

        4.void bzero(void *s, size_t n);

        5.bzero函数将指定内存块的前n个字节设置为0。

        6.服务器提供服务的端口一般选择大于1023,因为【0,1023】是系统内定的端口号

服务端核心代码

#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unordered_map>
#include "Log.hpp"
Log lg;enum{SOCKET_ERR=1,BIND_ERR
};
uint16_t defaultport=8080;
std::string defaultip="0.0.0.0";
const int size=1024;
class UdpServer
{
public:UdpServer(const uint16_t& port=defaultport,const std::string&ip=defaultip):_sockfd(0),_port(port),_ip(ip){} //初始化void Init(){//1.创建 udp socket//udp的socket是全双工的,允许被同时读写//AF_INET表示使用IPv4地址族  SOCK_DGRAM表示创建一个数据报套接字,0表示以阻塞的方式_sockfd=socket(AF_INET,SOCK_DGRAM,0);//创建套接字失败if(_sockfd<0){lg(Fatal,"socket create error,sockfd:%d",_sockfd);exit(SOCKET_ERR);}//创建套接字成功lg(Info,"socket create success,sockfd:%d",_sockfd);//2.bind socketstruct sockaddr_in local;bzero(&local,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(_port);//需要保证我的端口号是网络字节序列,因为该端口号是要给对方发送的local.sin_addr.s_addr=inet_addr(_ip.c_str()); //string->uint32_t必须是网络序列if(bind(_sockfd,(const struct sockaddr*)&local,sizeof(local))<0){//绑定失败lg(Fatal,"bind error,errno:%d,err string:%s",errno,strerror(errno));exit(BIND_ERR);}//绑定成功lg(Info,"bind success,errno:%d,err string:%s",errno,strerror(errno));}void CheckUser(const struct sockaddr_in& client,const std::string clientip,uint16_t clientport){auto iter=_online_user.find(clientip);if(iter==_online_user.end()){_online_user.insert({clientip,client});std::cout<<"["<<clientip<<":"<<clientport<<"] add to online user."<<std::endl;}}//对存在用户列表的所有用户进行转发void Broadcast(const std::string&info,const std::string clientip,uint16_t clientport){for(const auto& user:_online_user){std::string message="[";message += clientip;message += ":";message += std::to_string(clientport);message += "]# ";message += info;socklen_t len = sizeof(user.second);sendto(_sockfd,message.c_str(),message.size(),0,(struct sockaddr*)(&user.second),len);}}//开始运行void Run(){isrunning = true;char inbuffer[size];while(isrunning){struct sockaddr_in client;socklen_t len=sizeof(client);//接收客户端ssize_t n=recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,(struct sockaddr*)&client,&len);if(n<0){//未收到lg(Warning,"recvfrom error,errno:%d,err string:%s",errno,strerror(errno));continue;}uint16_t clientport=ntohs(client.sin_port);std::string clientip=inet_ntoa(client.sin_addr);//网络字节序列转换 string//检查该用户是否已在聊天室CheckUser(client,clientip,clientport);std::string info=inbuffer;//向其他成员转发Broadcast(info,clientip,clientport);}}
private:int _sockfd;uint16_t _port;std::string _ip;bool isrunning; //服务器是否开始运行std::unordered_map<std::string,struct sockaddr_in> _online_user; //用户列表
};

服务端运行代码

#include "UdpServer.hpp"
#include <memory>
#include <cstdio>
#include <vector>
void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n" << std::endl;
}
// ./udpserver port
int main(int argc, char *argv[])
{if(argc != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<UdpServer> svr(new UdpServer(port));svr->Init(/**/);svr->Run();return 0;
}

客户端设计

客户端也是需要绑定端口的,但是不需要用户显示绑定,一般由os自由随机选择,在首次发消息的时候绑定。不同于服务端的是:服务端端口号必须是唯一确定的,客户端可变。

#include <iostream>
#include <string>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Terminal.hpp"
using namespace std;struct ThreadDate
{struct sockaddr_in server;int sockfd;std::string serverip;
};void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<"serverip serverport\n"<<std::endl;
}
//收信息
void* recv_message(void* args)
{OpenTerminal();ThreadDate* td = static_cast<ThreadDate*>(args);char buffer[1024];while(true){memset(buffer,0,sizeof(buffer));struct sockaddr_in tmp;socklen_t len=sizeof(tmp);ssize_t n=recvfrom(td->sockfd,buffer,1023,0,(struct sockaddr*)&tmp,&len);if(n>0){buffer[n]='\0';cerr<<buffer<<endl;}}
}
//发信息
void* send_message(void* args)
{ThreadDate* td=static_cast<ThreadDate*>(args);string message;socklen_t len=sizeof(td->server);string welcome = td->serverip;welcome += "coming...";sendto(td->sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&(td->server),len);while(true){cout<<"Please Enter@ ";getline(cin,message);sendto(td->sockfd,message.c_str(),message.size(),0,(struct sockaddr*)&(td->server),len);}
}
//多线程
//./udpclient serverip serverporta
int main(int argc,char* argv[])
{if(argc!=3){Usage(argv[0]);exit(0);}std::string serverip=argv[1];uint16_t serverport=std::stoi(argv[2]);struct ThreadDate td;bzero(&td.server,sizeof(td.server));td.server.sin_family=AF_INET;td.server.sin_port=htons(serverport);td.server.sin_addr.s_addr=inet_addr(serverip.c_str());td.sockfd=socket(AF_INET,SOCK_DGRAM,0);if(td.sockfd<0){cout<<"scoket error"<<endl;return 1;}td.serverip=serverip;pthread_t recver,sender;pthread_create(&recver,nullptr,recv_message,&td);pthread_create(&sender,nullptr,send_message,&td);pthread_join(recver,nullptr);pthread_join(sender,nullptr);close(td.sockfd);return 0;
}

上述客户端为了用户交互友好,我们打开两个终端模拟,一个终端负责发信息,一个终端负责收信息显示,我们重定向客户端收到消息后,往第二个终端打印。

ls  -l  /dev/pts   //查看我们有哪些终端文件,显示它们的详细信息 

例如:

重定向输出信息

#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//原来的终端
std::string terminal = "/dev/pts/2";int OpenTerminal()
{int fd = open(terminal.c_str(), O_WRONLY);if(fd < 0){std::cerr << "open terminal error" << std::endl;return 1;}修改到要显示的终端dup2(fd, 0);// printf("hello world\n");// close(fd);return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

AB测试最小样本量

1.AB实验过程 常见的AB实验过程&#xff0c;分流-->实验-->数据分析-->决策&#xff1a;分流&#xff1a;用户被随机均匀的分为不同的组实验&#xff1a;同一组内的用户在实验期间使用相同的策略&#xff0c;不同组的用户使用相同或不同的策略。数据收集&#xff1a;…

【NodeJS】006- API模块与会话控制介绍d

1.简介 1.1 接口是什么 接口是 前后端通信的桥梁 简单理解&#xff1a;一个接口就是 服务中的一个路由规则 &#xff0c;根据请求响应结果 接口的英文单词是 API (Application Program Interface)&#xff0c;所以有时也称之为 API 接口 这里的接口指的是『数据接口』&#…

深度学习(15)--PyTorch构建卷积神经网络

目录 一.PyTorch构建卷积神经网络(CNN)详细流程 二.graphviz torchviz使PyTorch网络可视化 2.1.可视化经典网络vgg16 2.2.可视化自己定义的网络 一.PyTorch构建卷积神经网络(CNN)详细流程 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;是一种深度学…

SpringBoot源码解读与原理分析(七)BeanFactory

文章目录 3 SpringBoot的IOC容器3.1 SpringFramework的IOC容器3.1.1 BeanFactory3.1.1.1 BeanFactory根接口3.1.1.2 HierarchicalBeanFactory3.1.1.3 ListableBeanFactory3.1.1.4 AutowireCapableBeanFactory3.1.1.5 ConfigurableBeanFactory3.1.1.6 AbstractBeanFactory3.1.1.…

机器学习之指数分布

指数分布&#xff1a; 指数分布可以用来表示独立随机事件发生的时间间隔。如果一个随机变量X的概率密度函数满足以下形式&#xff0c;就称X服从参数λ的指数分布&#xff0c;记作X ~ E(λ)或X~Exp&#xff08;λ&#xff09;。指数分布只有一个指数参数&#xff0c;且λ>0&a…

SolidWorks学习笔记——入门知识2

目录 建出第一个模型 1、建立草图 2、选取中心线 3、草图绘制 4、拉伸 特征的显示与隐藏 改变特征名称 5、外观 6、渲染 建出第一个模型 1、建立草图 图1 建立草图 按需要选择基准面。 2、选取中心线 图2 选取中心线 3、草图绘制 以对称图形举例&#xff0c;先画出…

【GAMES101】Lecture 18 高级光线传播

这节课不涉及数学原理&#xff0c;只讲流程操作&#xff0c;大家当听这个十万个为什么就行 目录 高级光线传播 无偏光线传播方法 双向路径追踪&#xff08;Bidirectional path tracing) Metropolis light transport (MLT) 有偏光线传播方法 光子映射&#xff08;Photon …

FXTM富拓监管变更!2024开年连续3家交易商注销牌照

交易商的监管信息是经常发生变更的&#xff0c;即使第一次投资时查询平台监管牌照&#xff0c;投资者仍需持续关注其监管动态。千万不要以为第一步审核好后就万事大吉了&#xff01; 2024年开年&#xff0c;就有3家交易商的重要信息发生变更&#xff0c;注销其金融监管牌照&…

按键扫描16Hz-单片机通用模板

按键扫描16Hz-单片机通用模板 一、按键扫描的原理1、直接检测高低电平类型2、矩阵扫描类型3、ADC检测类型二、---.c的实现1、void keyScan(void) 按键扫描函数①void FHiKey(void) 按键按下功能②void FSameKey(void) 按键长按功能③void FLowKey(void) 按键释放功能三、key.h的…

Leetcode—135. 分发糖果【中等】

2024每日刷题&#xff08;113&#xff09; Leetcode—135. 分发糖果 算法思想 这里可以利用贪心策略&#xff0c;求局部最优解&#xff0c;然后合并为全局最优解。具体来说&#xff0c;将原问题中相邻孩子的条件划分为左相邻孩子和右相邻孩子两个条件&#xff0c;依次求解出两…

Phobos捆绑某数控软件AdobeIPCBroker组件定向勒索

前言 Phobos勒索病毒最早于2019年被首次发现并开始流行起来&#xff0c;该勒索病毒的勒索提示信息特征与CrySiS(Dharma)勒索病毒非常相似&#xff0c;但是两款勒索病毒的代码特征却是完全不一样&#xff0c;近日笔者在逛某开源恶意软件沙箱的时候发现了一款Phobos勒索病毒捆绑…

应用层DoS

应用层&#xff08;application layer&#xff09;是七层OSI模型的第七层。应用层直接和应用程序 对接并提供常见的网络应用服务&#xff0c;能够在实现多个系统应用进程相互通信的同 时&#xff0c;完成一系列业务处理所需的服务。位于应用层的协议有很多&#xff0c;常见的包…

【已解决】:pip is configured with locations that require TLS/SSL

在使用pip进行软件包安装的时候出现问题&#xff1a; WARNING: pip is configured with locations that require TLS/SSL, however the ssl module in Python is not available. 解决&#xff1a; mkdir -p ~/.pip vim ~/.pip/pip.conf然后输入内容&#xff1a; [global] ind…

07-OpenFeign-HTTP压缩优化

gzip是一种数据格式&#xff0c;采用用deflate算法压缩数据&#xff1b;gzip是一种流行的数据压缩算法&#xff0c;应用十分广泛&#xff0c;尤其是在Linux平台。 当GZIP压缩到一个纯文本数据时&#xff0c;效果是非常明显的&#xff0c;大约可以减少70&#xff05;以上的数据…

第九个知识点:内部对象

Date对象: <script>var date new Date();date.getFullYear();//年date.getMonth();//月date.getDate();//日date.getDay();//星期几date.getHours();//时date.getMinutes();//分date.getSeconds();//秒date.getTime();//获取时间戳&#xff0c;时间戳时全球统一&#x…

C++力扣题目494--目标和 474--一和零

494.目标和 力扣题目链接(opens new window) 难度&#xff1a;中等 给定一个非负整数数组&#xff0c;a1, a2, ..., an, 和一个目标数&#xff0c;S。现在你有两个符号 和 -。对于数组中的任意一个整数&#xff0c;你都可以从 或 -中选择一个符号添加在前面。 返回可以使…

Backtrader 文档学习- Plotting

Backtrader 文档学习- Plotting 虽然回测是一个基于数学计算的自动化过程&#xff0c;还是希望实际通过可视化验证。无论是使用现有算法回测&#xff0c;还是观察数据驱动的指标&#xff08;内置或自定义&#xff09;。 凡事都要有人完成&#xff0c;绘制数据加载、指标、操作…

PostgreSql与Postgis安装

POstgresql安装 1.登录官网 PostgreSQL: Linux downloads (Red Hat family) 2.选择版本 3.安装 ### 源 yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm ### 客户端 yum install postgresql14 ###…

Java面向对象 继承

目录 继承继承的好处继承具有传递性实例创建Person类Student继承Person类测试 继承 Java中的继承是面向对象编程的一个核心特性&#xff0c;它允许一个类&#xff08;子类或派生类&#xff09;继承另一个类&#xff08;父类或基类&#xff09;的属性和方法。通过继承&#xff0…

【HarmonyOS应用开发】HTTP数据请求(十四)

文章末尾含相关内容源代码 一、概述 日常生活中我们使用应用程序看新闻、发送消息等&#xff0c;都需要连接到互联网&#xff0c;从服务端获取数据。例如&#xff0c;新闻应用可以从新闻服务器中获取最新的热点新闻&#xff0c;从而给用户打造更加丰富、更加实用的体验。 那么…