【Linux】用C++实现UDP通信:详解socket编程流程

【Linux】用C++实现UDP通信:详解socket编程流程
在这里插入图片描述

文章目录

协议(Protocol)

协议 是计算机或通信系统中,不同实体(如设备、程序、服务等)之间进行交互和通信时,共同遵循的一套规则和标准。它定义了数据的格式、传输方式、错误处理、安全机制等,确保通信双方能够正确理解彼此的信息并完成协作。


协议的核心要素

  1. 语法(Syntax)
    • 数据的结构或格式,例如报文如何排列、字段的长度和顺序等。
    • 示例:HTTP请求中,请求行、头部、正文的排列方式。
  2. 语义(Semantics)
    • 数据的含义及操作逻辑,解释字段代表的动作或内容。
    • 示例:HTTP状态码 200 表示请求成功,404 表示资源未找到。
  3. 同步(Timing/Synchronization)
    • 通信的顺序控制,如数据发送和响应的时序。
    • 示例:TCP三次握手建立连接时的顺序规则。

常见协议分类

类别协议示例作用
网络通信TCP/IP、HTTP、FTP实现数据传输和网络互联
安全协议SSL/TLS、SSH、HTTPS加密通信和身份验证
应用层协议SMTP(邮件)、DNS支持特定应用功能(如邮件解析域名)
硬件协议USB、Bluetooth硬件设备间的交互规范

UDP协议(用户数据报协议)

1. 基本定义

UDP(User Datagram Protocol) 是一种无连接的传输层协议,位于TCP/IP模型中的传输层(OSI第4层)。它以最小化的协议机制提供高效的数据传输服务。

2. 核心特性

特性说明
无连接通信前无需建立连接,直接发送数据
不可靠传输不保证数据顺序、不重传丢失报文、不检测拥塞
无状态发送方和接收方不维护连接状态
头部开销小固定8字节头部(TCP至少20字节)
支持广播/多播可向多个主机同时发送数据

UDP协议实现通信

服务器端

由于UDP协议是一种通过数据报在网络中传输的协议,所以我们在创建套接字的时候需要将参数设置为数据报类型,服务器端主要有几个功能,一个是初始化服务器,一个是启动服务器,在启动服务器的时候需要将服务器写成死循环,服务器可以一直接收外部发来的数据。因为在服务器中存在着很多需要将网络字节序转化为本地字节序,所以为了方便,我们将IP地址和端口号封装成一个类InetAddr,这个类中的方法有网络字节序和本地字节序的转化,还有获取网络字节序和本地字节序,方便我们写代码的可读性。

注意:这里面用的LOG是上一章封装过的一个LOG类,可以直接拿过来用

Comm.hpp

#pragmaonce#include<iostream>#defineDie(code)exit(code)#defineCONV(v)(structsockaddr*)(v)enum{ USAGE_ERR, SOCKET_ERR, BIND_ERR };

InetAddr.hpp

#pragmaonce#include"Comm.hpp"#include<iostream>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<arpa/inet.h>classInetAddr{private:voidPortNet2Host(){ _port =::ntohl(_net_addr.sin_port);}voidIpNet2Host(){//将网络风格转化为字符串风格的char ipbuffer[64];constchar* ip =inet_ntop(AF_INET,&_net_addr.sin_addr,ipbuffer,sizeof(ipbuffer));(void)ip;}public:InetAddr(){}//网络转点分十进制---这里是网络转主机InetAddr(conststructsockaddr_in& addr):_net_addr(addr){PortNet2Host();IpNet2Host();}InetAddr(uint16_t port):_port(port),_ip(""){ _port = _net_addr.sin_family = AF_INET; _net_addr.sin_port =htons(_port);//这里是主机转网络 _net_addr.sin_addr.s_addr = INADDR_ANY;}structsockaddr*NetAddr(){returnCONV(&_net_addr);} socklen_t NetAddrLen(){returnsizeof(_net_addr);} std::string Ip(){return _ip;} std::uint16_tPort(){return _port;}~InetAddr(){}private://网络addrstructsockaddr_in _net_addr;//网络序列的端口uint16_t _port; std::string _ip;};

UdpServer.hpp

#ifndef__UDP_SERVER_HPP__#define__UDP_SERVER_HPP__#include"InetAddr.hpp"#include"Comm.hpp"#include<iostream>#include<string>#include<memory>#include<sys/socket.h>#include<sys/types.h>#include<netinet/in.h>#include<arpa/inet.h>#include<strings.h>#include<string.h>#include"Log.hpp"usingnamespace lyrics;conststaticint gsockfd =-1;// 这个ip表示主机的本地IP,一般用于做本地通信的conststatic std::string gdefaultip ="127.0.0.1";// 测试用的端口号conststaticuint16_t defaultport =8000;classUdpServer{public:UdpServer(uint16_t port = defaultport):_sockfd(gsockfd),_addr(port),_isrunning(false){}// 1. 初始化服务器----都是套路---UDP初始化voidInitServer(){// 创建套接字 _sockfd =socket(AF_INET, SOCK_DGRAM,0);// IP?PORT?网络?本地?if(_sockfd <0){// 输出致命的错误LOG(LogLevel::FATAL)<<"socket: "<<strerror(errno);// 创建失败直接结束:Die(SOCKET_ERR);}LOG(LogLevel::INFO)<<"socket success,sockfd is: "<< _sockfd;int n =::bind(_sockfd,_addr.NetAddr(), _addr.NetAddrLen());if(n <0){LOG(LogLevel::FATAL)<<"socket: "<<strerror(errno);Die(BIND_ERR);}LOG(LogLevel::INFO)<<"bind success";}voidStart(){ _isrunning =true;//服务器死循环while(true){structsockaddr_in peer;//远端 socklen_t len =sizeof(peer);//必须设定char inbuffer[1024]; ssize_t n =::recvfrom(_sockfd,inbuffer,sizeof(inbuffer)-1,0,CONV(&peer),&len);//读取消息成功if(n >0){ InetAddr cli(peer);//1. 需要知道消息内容 inbuffer[n]=0; std::string clientinfo = cli.Ip()+":"+ std::to_string(cli.Port())+" # "+ inbuffer;LOG(LogLevel::DEBUG)<< clientinfo; std::string echo_string ="echo "; echo_string += inbuffer;//2. 得知道消息是谁发的::sendto(_sockfd,echo_string.c_str(),echo_string.size(),0,CONV(&peer),sizeof(peer));}} _isrunning =false;}~UdpServer(){if(_sockfd > gsockfd)::close(_sockfd);}private:int _sockfd; InetAddr _addr;// uint16_t _port; // 服务器未来的端口号// 传递进来的是字符串风格的IP----点分十进制的IP地址//std::string _ip; // 服务器未来的ip地址bool _isrunning;// 服务器的运行状态};#endif

UdpServer.cc

#include"UdpServer.hpp"// ./server_udp localip localportintmain(int argc,char*argv[]){if(argc !=2){ std::cerr <<"Usage"<< argv[0]<<"localport"<< std::endl;Die(USAGE_ERR);}//默认输出在显示器上ENABLE_CONSOLE_LOG(); std::unique_ptr<UdpServer> svr_uptr = std::make_unique<UdpServer>(std::stoi(argv[1])); svr_uptr->InitServer(); svr_uptr->Start();return0;}

客户端

客户端不用封装,只需要创建套接字,然后向目标IP和目标端口号发送数据即可,在发送数据的时候需要写成死循环,这样客户端可以一直向服务器发送消息,我们发送一条消息,客户端回一条一模一样的消息,表示服务器接收到了消息。

#include"UdpClient.hpp"#include"Comm.hpp"#include<iostream>#include<netinet/in.h>#include<arpa/inet.h>#include<sys/types.h>#include<sys/socket.h>#include<string.h>#include<cstdlib>#include<string>// ./client_udp serverip serverport----客户端需r要先知道服务器的端口号和IPintmain(int argc,char*argv[]){if(argc !=3){ std::cerr <<"Usage"<< argv[0]<<"serverip serverport"<< std::endl;Die(USAGE_ERR);} std::string serverip = argv[1];// 命令行上输入的都是字符串uint16_t serverport = std::stoi(argv[2]);// 1. 创建套接字int sockfd =::socket(AF_INET, SOCK_DGRAM,0);if(sockfd <0){ std::cerr <<"socket error"<< std::endl;Die(SOCKET_ERR);}// 1.1 填充server信息structsockaddr_in server;memset(&server,0,sizeof(server)); server.sin_family = AF_INET;// 需要调用主机转网络 server.sin_port =htons(serverport); server.sin_addr.s_addr =::inet_addr(serverip.c_str());// 2. clientdonewhile(true){ std::cout <<"please Entrer: "; std::string message; std::getline(std::cin, message);int n =::sendto(sockfd, message.c_str(), message.size(),0,CONV(&server),sizeof(server));(void)n;structsockaddr_in temp; socklen_t len =sizeof(temp);char buffer[1024]; n =::recvfrom(sockfd,buffer,sizeof(buffer)-1,0,CONV(&temp),&len);if(n >0){ buffer[n]=0; std::cout << buffer <<std::endl;}}return0;}

总结

至此,我们用C++完整实现了一个基于UDP的通信流程,从创建 socket、绑定地址,到收发数据、关闭连接,每一步都围绕 Linux 下的 socket 编程核心展开。虽然 UDP 天生“无连接、不可靠”,但正因如此,它在低延迟、高并发场景下依然扮演着重要角色。希望这篇博客不仅帮你理清了 UDP 的基本用法,也为你后续深入网络编程打下了坚实的地基。别忘了,代码看完了不算真学会,敲一遍才是你自己的!

Read more

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

摘要:本文聚焦OpenClaw从测试环境走向生产环境的核心痛点,围绕“性能优化、安全加固、监控运维”三大维度展开实操讲解。先明确生产环境硬件/系统选型标准,再通过硬件层资源管控、模型调度策略、缓存优化等手段提升响应速度(实测响应效率提升50%+);接着从网络、权限、数据三层构建安全防护体系,集成火山引擎安全方案拦截高危操作;最后落地TenacitOS可视化监控与Prometheus告警体系,配套完整故障排查清单和虚拟实战案例。全文所有配置、代码均经实测验证,兼顾新手入门实操性和进阶读者的生产级部署需求,帮助开发者真正实现OpenClaw从“能用”到“放心用”的跨越。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:

By Ne0inhk
ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

🎬 渡水无言:个人主页渡水无言 ❄专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》 ❄专栏传送门: 《freertos专栏》《STM32 HAL库专栏》 ⭐️流水不争先,争的是滔滔不绝  📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生 | 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生 在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连 目录 前言  一、实验基础说明 1.1、互斥体简介 1.2 本次实验设计思路 二、硬件原理分析(看过之前博客的可以忽略) 三、实验程序编写 3.1 互斥体 LED 驱动代码(mutex.c) 3.2.1、设备结构体定义(28-39

By Ne0inhk
Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 后端工程师扔给你一个 Swagger (OpenAPI) 文档地址,你会怎么做? 1. 对着文档,手写 Dart Model 类(容易写错字段类型)。 2. 手写 Retrofit/Dio 的 API 接口定义(容易拼错 URL)。 3. 当后端修改了字段名,你对着报错修半天。 这是重复劳动的地狱。 swagger_dart_code_generator 可以将 Swagger (JSON/YAML) 文件直接转换为高质量的 Dart 代码,包括: * Model 类:支持 json_serializable,带 fromJson/

By Ne0inhk
Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

文章目录 * 前言 * make/makefile * 文件的三个时间 * Linux第一个小程序-进度条 * 回车和换行 * 缓冲区 * 程序的代码展示 * git指令 * 关于gitee * Linux调试器-gdb使用 * 作业部分 前言 做 Linux 开发时,你是不是也遇到过这些 “卡脖子” 时刻?写 makefile 时,明明语法没错却报错,最后发现是依赖方法行没加 Tab;想提交代码到 gitee,记不清 git add/commit/push 的 “三板斧”,还得反复搜教程;用 gdb 调试程序,输了命令没反应,才想起编译时没加-g生成 debug 版本;甚至连写个进度条,都搞不懂\r和\n的区别,导致进度条乱跳…… 其实这些问题,

By Ne0inhk