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

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

一、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.lryc.cn/news/2394875.html

相关文章:

  • 嵌入式学习笔记 - keil安装目录下的头文件自动包含问题
  • word批量导出visio图
  • 把数据库做得能扩展:Aurora DSQL 的故事
  • 全面解析:npm 命令、package.json 结构与 Vite 详解
  • 【本地部署】 Deepseek+Dify创建工作流
  • Rust 配置解析`serde` + `toml`
  • linux进程用户态内存泄露问题从进程角度跟踪举例
  • 数据结构-图的应用,实现环形校验和拓扑排序
  • 交换机 路由器
  • 某乎x-zse-96 破解(补环境版本)
  • VSCode+Cline 安装配置及使用说明
  • Java中Redis面试题集锦(含过期策略详解)
  • Maven 安装与配置指南(适用于 Windows、Linux 和 macOS)
  • android 媒体框架之MediaCodec
  • 堆与堆排序及 Top-K 问题解析:从原理到实践
  • Linux中检查当前用户是不是root
  • 软件锁:守护隐私,安心无忧
  • 无人机桥梁3D建模、巡检、检测的航线规划
  • 项目:贪吃蛇实现
  • 【Java基础05】面向对象01
  • 设计模式:观察者模式 - 实战
  • 8.8 Primary ODSA service without ODSA Portal
  • YOLOv8 移动端升级:借助 GhostNetv2 主干网络,实现高效特征提取
  • 国产化Word处理控件Spire.Doc教程:在 C# 中打印 Word 文档终极指南
  • java的vscode扩展插件
  • 谷歌:贝叶斯框架优化LLM推理反思
  • Qt SQL模块基础
  • [9-3] 串口发送串口发送+接收 江协科技学习笔记(26个知识点)
  • java 微服务中,微服务相互调用 feign 和flux 如何选择
  • 如何在Qt中绘制一个带有动画的弧形进度条?