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

news2025/6/3 10:46:45

一、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_;//ip
    std::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.bind
        if(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.cpp
	g++ -o $@ $^ -std=c++11 -lpthread
udpserver:Main.cpp
	g++ -o $@ $^ -std=c++11
.PHONY:clean
clean:
	rm -f udpclient udpserver

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

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

相关文章

word批量导出visio图

具体步骤 修改word格式打开VBA窗口插入代码运行代码 修改word格式 将word文档修改为docm格式 打开VBA窗口 打开开发工具VisualBasic项&#xff0c;如果没有右键在自定义功能区添加 插入代码 插入 -> 模块&#xff0c;代码如下&#xff1a; Sub ExportAllVisioDiagrams()D…

【本地部署】 Deepseek+Dify创建工作流

文章目录 DeepseekDify 简介流程1、下载Docker2、Dify下载3、使用浏览器打开 Deepseek Deepseek 是一款功能强大的 AI 语言模型工具&#xff0c;具备出色的理解与生成能力。它可以处理各种自然语言任务&#xff0c;无论是文本创作、问答&#xff0c;还是数据分析与解释&#x…

数据结构-图的应用,实现环形校验和拓扑排序

文章目录 一、如何理解“图”&#xff1f;1.什么是图&#xff1f;2.无向图和有向图3.无权图和有权图 二、JGraphT-图论数据结构和算法的 Java 库1.引入Maven依赖2.环形校验2.1 什么是循环依赖 &#xff1f;2.2 单元测试代码2.3 情况1&#xff1a;自己依赖自己2.4 情况2&#xf…

某乎x-zse-96 破解(补环境版本)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、总体概述二、请求分析分析请求流程三、逆向分析总结一、总体概述 本文主要实现某乎x-zse-96 破解(补环境版本),相关的链接: https://www.zhihu.com/search?type=content&q=%25E7%258…

VSCode+Cline 安装配置及使用说明

安装配置 打开VSCode&#xff0c;点击左侧Extension图标&#xff0c;在弹出页面中&#xff0c;检索Cline&#xff0c;选择Cline进行安装。 安装完毕&#xff0c;在左侧会出现一个图标&#xff0c;点击图标 选择【Use your own API key】&#xff0c;在出来的界面中选择大模型&…

Maven 安装与配置指南(适用于 Windows、Linux 和 macOS)

Apache Maven 是一款广泛应用于 Java 项目的项目管理和构建工具。 本文提供在 Windows、Linux 和 macOS 系统上安装与配置 Maven 的详细步骤&#xff0c;旨在帮助开发者快速搭建高效的构建环境。 一、前置条件&#xff1a;安装 Java Development Kit (JDK) Maven 依赖于 Java …

android 媒体框架之MediaCodec

一、MediaCodec 整体架构与设计思想 MediaCodec 是 Android 底层多媒体框架的核心组件&#xff0c;负责高效处理音视频编解码任务。其架构采用 生产者-消费者模型&#xff0c;通过双缓冲区队列&#xff08;输入/输出&#xff09;实现异步数据处理&#xff1a; 输入缓冲区队列…

堆与堆排序及 Top-K 问题解析:从原理到实践

一、堆的本质与核心特性 堆是一种基于完全二叉树的数据结构&#xff0c;其核心特性为父节点与子节点的数值关系&#xff0c;分为大堆和小堆两类&#xff1a; 大堆&#xff1a;每个父节点的值均大于或等于其子节点的值&#xff0c;堆顶元素为最大值。如: 小堆&#xff1a;每个…

软件锁:守护隐私,安心无忧

数字化时代&#xff0c;手机已成为我们生活中不可或缺的一部分&#xff0c;它不仅存储着我们的个人信息、照片、聊天记录等重要数据&#xff0c;还承载着我们的社交、娱乐和工作等多种功能。然而&#xff0c;这也意味着手机上的隐私信息面临着诸多泄露风险。无论是家人、朋友还…

无人机桥梁3D建模、巡检、检测的航线规划

无人机桥梁3D建模、巡检、检测的航线规划 无人机在3D建模、巡检和检测任务中的航线规划存在显著差异&#xff0c;主要体现在飞行高度、航线模式、精度要求和传感器配置等方面。以下是三者的详细对比分析&#xff1a; 1. 核心目标差异 任务类型主要目标典型应用场景3D建模 生成…

【Java基础05】面向对象01

文章目录 1. 设计对象并使用1.1 类与对象1.2 封装1.2.1 private关键字1.2.2 this关键字成员变量和局部变量的区别 1.2.3 构造方法1.2.4 标准JavaBean类 1.3 对象内存图 本文部分参考这篇博客 1. 设计对象并使用 1.1 类与对象 public class 类名{1、成员变量(代表属性,一般是名…

设计模式:观察者模式 - 实战

一、观察者模式场景 1.1 什么是观察者模式&#xff1f; 观察者模式&#xff08;Observer Pattern&#xff09;观察者模式是一种行为型设计模式&#xff0c;用于定义一种一对多的依赖关系&#xff0c;当对象的状态发生变化时&#xff0c;所有依赖于它的对象都会自动收到通知并更…

YOLOv8 移动端升级:借助 GhostNetv2 主干网络,实现高效特征提取

文章目录 引言GhostNetv2概述GhostNet回顾GhostNetv2创新 YOLOv8主干网络改进原YOLOv8主干分析GhostNetv2主干替换方案整体架构设计关键模块实现 完整主干网络实现YOLOv8集成与训练模型集成训练技巧 性能对比与分析计算复杂度对比优势分析 部署优化建议结论与展望 引言 目标检…

国产化Word处理控件Spire.Doc教程:在 C# 中打印 Word 文档终极指南

在 C# 中以编程方式打印 Word 文档可以简化业务工作流程、自动化报告和增强文档管理系统。本指南全面探讨如何使用Spire.Doc for .NET打印 Word 文档&#xff0c;涵盖从基本打印到高级自定义技术的所有内容。我们将逐步介绍每种情况下的实际代码示例&#xff0c;确保您能够在实…

谷歌:贝叶斯框架优化LLM推理反思

&#x1f4d6;标题&#xff1a;Beyond Markovian: Reflective Exploration via Bayes-Adaptive RL for LLM Reasoning &#x1f310;来源&#xff1a;arXiv, 2505.20561 &#x1f31f;摘要 通过强化学习 (RL) 训练的大型语言模型 (LLM) 表现出强大的推理能力和紧急反射行为&a…

Qt SQL模块基础

Qt SQL模块基础 一、Qt SQL模块支持的数据库 官方帮助文档中的Qt支持的数据库驱动如下图&#xff1a; Qt SQL 模块中提供了一些常见的数据库驱动&#xff0c;包括网络型数据库&#xff0c;如Qracle、MS SQL Server、MySQL等&#xff0c;也包括简单的单机型数据库。 Qt SQL支…

[9-3] 串口发送串口发送+接收 江协科技学习笔记(26个知识点)

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26中断

如何在Qt中绘制一个带有动画的弧形进度条?

如何在Qt中绘制一个弧形的进度条 在图形用户界面开发中&#xff0c;进度指示控件&#xff08;Progress Widget&#xff09;是非常常见且实用的组件。CCArcProgressWidget 是一个继承自 QWidget 的自定义控件&#xff0c;用于绘制圆弧形进度条。当然&#xff0c;笔者看了眼公开…

国产三维CAD皇冠CAD(CrownCAD)建模教程:汽车电池

在线解读『汽车电池』的三维建模流程&#xff0c;讲解3D草图、保存实体、拉伸凸台/基体、设置外观等操作技巧&#xff0c;一起和皇冠CAD&#xff08;CrownCAD&#xff09;学习制作步骤吧&#xff01; 汽车电池&#xff08;通常指铅酸蓄电池或锂离子电池&#xff09;是车辆电气系…

VMware-workstation安装教程--超详细(附带安装包)附带安装CentOS系统教程

VMware-workstation安装教程--超详细&#xff08;附带安装包&#xff09;附带安装CentOS系统教程 一、下载软件VMwware二、下载需要的镜像三、在VMware上安装系统 一、下载软件VMwware 二、下载需要的镜像 三、在VMware上安装系统 VMware 被 Broadcom&#xff08;博通&#x…