Linux --UDP套接字实现简单的网络聊天室

article/2025/6/20 14:26:04

一、Server端的实现

1.1、服务端的初始化

①、创建套接字:

创建套接字接口:

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
//1. 这是一个创建套接字的接口
//2. domain: 协议家族类型,其中包括ipv4、ipv6、等等,通常设置为ipv4就行,即AF_INET
//3. type: 套接字指定类型,其中SOCK_DGRAM是UDP类型,SOCK_STREAM是TCP类型
//4. 返回值int: 创建失败返回值小于0并设置错误码

②、绑定前的准备工作:

 

③、绑定套接字:

绑定接口:

//1. 这是一个绑定套接字的接口

//2. socket: 需要绑定的套接字

//3. address: sockaddr结构体地址

//4. address_len: sockaddr结构体大小

//5. 返回值int,绑定成功返回0,失败返回-1并设置错误码

1.2、服务端运行

①、接收信息

接收信息接口:

//1. socket: 从那个套接字接收信息

//2. buffer:接收的信息存放在哪个缓冲区; length: 缓冲区的大小

//3. flags: 控制接收行为的标志,通常设为0

//4. address: sockaddr结构体的地址(输入型参数,用来获取对方信息)

//5. address_len: sockaddr结构体大小

//6. 返回值ssize_t 成功返回接收到的字节数,如果没有数据可读或者套接字关闭返回0,失败返回-1,并设置errno错误码

②、接收信息的同时获取对方信息

③、实现两个方法,一个用来检测用户登陆;一个用来把用户发来的消息转发给每一个用户

到这里,服务端的基本框架完成,然后我们在实现以下客户端吧!

二、客户端的实现

2.1、创建套接字

2.2、准备数据并绑定(客户端不需要自己绑定,OS自动绑定)

2.3、用两个线程来执行收发任务

2.4、收发信息的实现

OK,以上客户端跟服务端都已搭建完成,那我们来测试一下吧

三、测试

3.1、本地测试

客户端:

这里我把内容重定向到另一个终端上,避免它输入到同一个终端上

服务端:

3.2、跨平台测试

四、最终代码

4.1 server
#pragma once
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string>
#include"log.hpp"
#include<unordered_map>Log lg;
const uint16_t defaultport=8808;
const std::string defaultip="0.0.0.0";
enum{SOCK_ERR=1,BIND_ERR
};
class UdpServer
{private:int sock_fd;//网络文件描述符uint16_t port_;//端口std::string ip_;//ipstd::unordered_map<std::string, sockaddr_in> online_user;public:UdpServer(const uint16_t&port=defaultport,const std::string &ip=defaultip):port_(port),ip_(ip){}void Initialize(){//1.创建套接字sock_fd=socket(AF_INET,SOCK_DGRAM,0);if(sock_fd<0){lg(FATAL,"Sock create fail!! errno is: %d, errstring is: %s",errno,strerror(errno));exit(SOCK_ERR);}lg(INFO,"Sock create sucess!! sockfd is: %d",sock_fd);//2.数据准备struct sockaddr_in server;memset(&server,0,sizeof(server));//初始化结构体inet_aton(ip_.c_str(),&server.sin_addr);//字符串转地址server.sin_family=AF_INET;server.sin_port=htons(port_);//主机转网络字节序//3.bindif(bind(sock_fd,(sockaddr*)&server,sizeof(server))<0){lg(FATAL,"Bind fail!! errno is: %d,errstring is: %s",errno,strerror(errno));exit(BIND_ERR);}lg(INFO,"Bind sucess!! sockfd is: %d",sock_fd);}void CheckUser(const uint16_t&port,const std::string&ip,sockaddr_in&client){auto iter=online_user.find(ip);if(iter!=online_user.end())return;online_user.insert({ip,client});std::cout<<"["<<port<<":"<<ip<<"]"<<"# user login...."<<std::endl;}void Broadcast(const uint16_t&port,const std::string&ip,const std::string& info){std::string message="[";message+=std::to_string(port);message+=":";message+=ip;message+="]#:";std::string echo_message=message+info;for(auto &e:online_user){sendto(sock_fd,echo_message.c_str(),echo_message.size(),0,(sockaddr*)(&e.second),sizeof(e.second));}}void Start(){char buffer[1024];//读入的缓冲区while(true){//1.读struct sockaddr_in client;//用来获取客户端信息socklen_t len=sizeof(client);ssize_t n=recvfrom(sock_fd,buffer,sizeof(buffer),0,(sockaddr*)&client,&len);if(n<0){continue;//读取失败继续读}else if(n>0){buffer[n]='\0';std::string info=buffer;uint16_t clientport=ntohs(client.sin_port);//网络转主机字节序char ipstr[32];inet_ntop(AF_INET,&client.sin_addr,ipstr,sizeof(ipstr));//地址转字符串std::string clientip=ipstr;CheckUser(clientport,clientip,client);//检查用户是否存在Broadcast(clientport,clientip,info);//转发客户端的信息给每个用户}}}~UdpServer(){if(sock_fd>0)close(sock_fd);}};
#include<iostream>
#include"UdpServer.hpp"
#include<memory>void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<" serverport[1024+]"<<std::endl;
}
// server serverport
int main(int argc,char*argv[])
{if(argc!=2){Usage(argv[0]);exit(1);}uint16_t serverport=std::stoi(argv[1]);std::unique_ptr<UdpServer> ptr(new UdpServer(serverport));ptr->Initialize();ptr->Start();return 0;
}

4.2 client
#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<cstring>
struct ThreadData
{int sock_fd;std::string serverip_;struct sockaddr_in server;};
void Usage(std::string proc)
{std::cout<<"\n\rUsage:"<<proc<<" serverip serverport"<<std::endl;
}
void* send_message(void*args)
{ThreadData* td=static_cast<ThreadData*>(args);std::string line;while(true){std::cout<<"Please enter@"<<std::endl;std::getline(std::cin,line);sendto(td->sock_fd,line.c_str(),line.size(),0,(sockaddr*)&td->server,sizeof(td->server));}return nullptr;
}
void* recv_message(void*args)
{ThreadData* td=static_cast<ThreadData*>(args);char buffer[1024];while(true){memset(buffer,0,sizeof(buffer));struct sockaddr_in temp;socklen_t templen=sizeof(temp);ssize_t n=recvfrom(td->sock_fd,buffer,sizeof(buffer)-1,0,(sockaddr*)&temp,&templen);if(n>0){buffer[n]='\0';std::cerr<<buffer<<std::endl;}}return nullptr;
}
//udpclient srverip server port
int main(int argc,char*argv[])
{if(argc!=3){Usage(argv[0]);exit(1);}ThreadData td;td.serverip_=argv[1];uint16_t serverport=std::stoi(argv[2]);//1.创建套接字td.sock_fd= socket(AF_INET,SOCK_DGRAM,0);if(td.sock_fd<0){return 2;}//2.准备数据memset(&td.server,0,sizeof(td.server));//初始化inet_pton(AF_INET,td.serverip_.c_str(),&td.server.sin_addr);//串转地址td.server.sin_family=AF_INET;td.server.sin_port=htons(serverport);//主机转网络字节序//3.需要绑定,OS自动绑定pthread_t send,recv;pthread_create(&send,nullptr,send_message,&td);pthread_create(&recv,nullptr,recv_message,&td);pthread_join(send,nullptr);pthread_join(recv,nullptr);return 0;
}
 4.3 makefile
.PHONY:all
all: udpserver udpclient
udpclient:UdpClient.cppg++ -o $@ $^ -std=c++11 -lpthread
udpserver:Main.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f udpclient udpserver


http://www.hkcw.cn/article/rGJtRMRXIi.shtml

相关文章

OpenHarmony标准系统-HDF框架之音频驱动开发

文章目录 引言OpenHarmony音频概述OpenHarmony音频框图HDF音频驱动框架概述HDF音频驱动框图HDF音频驱动框架分析之音频设备驱动HDF音频驱动框架分析之supportlibs实现HDF音频驱动框架分析之hdi-passthrough实现HDF音频驱动框架分析之hdi-bindev实现HDF音频驱动加载过程HDF音频驱…

C#WinForm程序时方法很多时Form.cs文件会很长,如何分别写入多个文件,partial class的作用体现出来了。

右键->添加->类 类文件名称为 FormButtonClick.cs 双击button3&#xff0c;将Form1里button3的Click事件处理方法拷贝到FormButtonClick.cs里面。

关于win10系统中环境变量path变成一行显示的问题

怎么把环境变量从一行显示恢复成列表显示(原文链接在最下面&#xff0c;感谢) 一行显示&#xff08;调整了环境变量把C:\Windows\System64开头的挪到了后面或者删了就会这样&#xff09;&#xff1a; 只需在开头加上 C:\Windows\System64; 重新打开 就恢复成列表显示了 关于wi…

NW969NW978美光闪存颗粒NW980NW984

NW969NW978美光闪存颗粒NW980NW984 技术解析&#xff1a;NW969、NW978、NW980与NW984的架构创新 美光&#xff08;Micron&#xff09;的闪存颗粒系列&#xff0c;尤其是NW969、NW978、NW980和NW984&#xff0c;代表了存储技术的前沿突破。这些产品均采用第九代3D TLC&#xf…

python打卡训练营打卡记录day41

知识回顾 数据增强卷积神经网络定义的写法batch归一化&#xff1a;调整一个批次的分布&#xff0c;常用与图像数据特征图&#xff1a;只有卷积操作输出的才叫特征图调度器&#xff1a;直接修改基础学习率 卷积操作常见流程如下&#xff1a; 1. 输入 → 卷积层 → Batch归一化层…

某航参数逆向及设备指纹分析

文章目录 1. 写在前面2. 接口分析3. 加密分析4. 算法还原5. 设备指纹风控分析与绕过 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享…

电子电器架构 --- OTA测试用例分析(上)

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 钝感力的“钝”,不是木讷、迟钝,而是直面困境的韧劲和耐力,是面对外界噪音的通透淡然。 生活中有两种人,一种人格外在意别人的眼光;另一种人无论…

方案精读:42页华为企业组织活力设计方案【附全文阅读】

该文档聚焦华为企业组织活力设计,核心内容为:在非线性时代,方向未必完全正确时,组织活力是企业成功关键,可通过创新与柔性激发。以熵增、熵减为理论基础,华为活力引擎模型包含宏观(厚积薄发、开放合作对抗熵增)与微观(人力资源管理对抗个人惰怠)。 实践上从三层面激活…

双目相机深度的误差分析(基线长度和相机焦距的选择)

全文基于针孔模型和基线水平放置来讨论 影响双目计算深度的因素&#xff1a; 1、基线长度&#xff1a;两台相机光心之间距离2、相机焦距&#xff08;像素&#xff09;&#xff1a; f x f_x fx​&#xff08;或 f y f_y fy​&#xff09;为焦距 f f f和一个缩放比例的乘积。在…

Namespace 命名空间的使用

名字空间&#xff1a;划分更多的逻辑空间&#xff0c;有效避免名字冲突的问题 1.什么是命名空间 名字命名空间 namespace 名字空间名 {...} // 名字空间 n1 域 namespace n1 {// 全局变量int g_money 0;void save(int money){g_money money;}void pay(int money){g_money - m…

力扣热题100之翻转二叉树

题目 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 代码 方法一&#xff1a;递归 # Definition for a binary tree node. # class TreeNode: # def __init__(self, val0, leftNone, rightNone): # self.val val # …

simulink mask的使用技巧

1.mask界面布局 1.1如何调整控件的位置和控件大小&#xff1f; 反正2020a是调不了&#xff0c; 找了好久&#xff0c;只能是调布局&#xff0c;例如你要调成下面这样&#xff1a; 第一个控件的iTem location属性选择New row 后面跟着的几个和第一个同一行的空间属性选择Cu…

第12讲、Odoo 18 权限控制机制详解

目录 引言权限机制概述权限组&#xff08;Groups&#xff09;访问控制列表&#xff08;ACL&#xff09;记录规则&#xff08;Record Rules&#xff09;字段级权限控制按钮级权限控制菜单级权限控制综合案例&#xff1a;多层级权限控制最佳实践与注意事项总结 引言 Odoo 18 提…

AIGC学习笔记(8)——AI大模型开发工程师

文章目录 AI大模型开发工程师007 LangChain之Model IO模块1 Model IO核心概念2 Model IO代码实战什么是LCEL&#xff1f;ModelModel的分类LLMsChatModel PromptPrompt templatesExample selectorsOutput parsers AI大模型开发工程师 007 LangChain之Model IO模块 1 Model IO核…

Java 文件操作 和 IO(5)-- 综合案例练习 -- 示例一

题目描述&#xff1a;扫描指定目录&#xff0c;并找到名称中包含指定字符的所有普通文件&#xff08;不包含目录&#xff09;&#xff0c;并且后续询问用户是否要删除该文件 文章目录 题目描述&#xff1a;扫描指定目录&#xff0c;并找到名称中包含指定字符的所有普通文件&…

Leetcode 465. 最优账单平衡

1.题目基本信息 1.1.题目描述 给你一个表示交易的数组 transactions &#xff0c;其中 transactions[i] [fromi, toi, amounti] 表示 ID fromi 的人给 ID toi 的人共计 amounti $ 。 请你计算并返回还清所有债务的最小交易笔数。 1.2.题目地址 https://leetcode.cn/pro…

【沉浸式求职学习day51】【发送邮件】【javaweb结尾】

沉浸式求职学习 邮件发送原理及实现1.概述2.简单邮件3.复杂邮件 网站注册发送邮件功能实现 邮件发送原理及实现 1.概述 传输协议 SMTP协议 发送邮件&#xff1a; 我们通常把处理用户smtp请求(邮件发送请求)的服务器称之为SMTP服务器(邮件发送服务器)。POP3协议 接收邮件&#…

标题:2025海外短剧爆发年:APP+H5双端系统开发,解锁全球流量与变现新大陆

描述&#xff1a; 2025年出海新风口&#xff01;深度解析海外短剧系统开发核心&#xff08;APPH5双端&#xff09;&#xff0c;揭秘高效开发策略与商业化路径&#xff0c;助您抢占万亿美元市场&#xff01; 全球娱乐消费模式正在剧变。2025年&#xff0c;海外短剧市场已从蓝海…

uni-app学习笔记十六-vue3页面生命周期(三)

uni-app官方文档页面生命周期部分位于页面 | uni-app官网。 本篇再介绍2个生命周期 1.onUnload&#xff1a;用于监听页面卸载。 当页面被关闭时&#xff0c;即页面的缓存被清掉时触发加载onUnload函数。 例如:在demo6页面点击跳转到demo4&#xff0c;在demo4页面回退不了到d…

钉钉红包性能优化之路

一、业务背景 请客红包、小礼物作为饿了么自研的业务产品&#xff0c;在钉钉的一方化入口中常驻&#xff0c;作为高UV、PV的toB产品&#xff0c;面对不同设备环境的用户&#xff0c;经常会偶尔得到一些用户反馈&#xff0c;如【页面白屏太久了】、【卡住了】等等&#xff0c;本…