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

UDP的使用

代码:

udp_base.h

#ifndef UDP_BASE_H
#define UDP_BASE_H#include <iostream>
#include <string>
#include <cstring>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>// IP地址转换函数
#include <system_error>class UDPBase {
protected:int sockfd; // socket文件描述符,-1表示无效struct sockaddr_in address; // 网络地址结构体,存储IP和端口信息bool is_initialized; // 标记socket是否已初始化public:UDPBase() : sockfd(-1), is_initialized(false) {} // 构造函数,初始化成员变量virtual ~UDPBase() { // 虚析构函数,确保正确清理资源if (sockfd != -1) { // 如果socket有效close(sockfd); // 关闭socket}}// 初始化socketbool initialize() {sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP socket// AF_INET: IPv4协议族// SOCK_DGRAM: 数据报socket(UDP)// 0: 默认协议if (sockfd < 0) { // socket创建失败std::cerr << "Socket creation failed: " << strerror(errno) << std::endl;return false;}is_initialized = true; // 标记为已初始化return true;}// 发送数据bool sendData(const std::string& message, const struct sockaddr_in& dest_addr) {if (!is_initialized) { // 检查socket是否初始化std::cerr << "Socket not initialized" << std::endl;return false;}// 发送数据到指定地址ssize_t sent_bytes = sendto(sockfd,           // socket描述符message.c_str(),   // 要发送的数据缓冲区message.length(),  // 数据长度0,                 // 标志位(通常为0)(const struct sockaddr*)&dest_addr, // 目标地址sizeof(dest_addr)); // 地址结构体大小if (sent_bytes < 0) {// 发送失败std::cerr << "Send failed: " << strerror(errno) << std::endl;return false;}std::cout << "Sent " << sent_bytes << " bytes" << std::endl;return true;// 发送成功}// 接收数据//struct sockaddr_in& sender_addr - 引用参数,用于输出发送方的地址信息//int timeout_ms = 0 - 超时时间(毫秒),默认值0表示阻塞模式std::string receiveData(struct sockaddr_in& sender_addr, int timeout_ms = 0) {if (!is_initialized) {//检查socket是否已初始化throw std::runtime_error("Socket not initialized");}// 设置超时(可选)if (timeout_ms > 0) {struct timeval tv;tv.tv_sec = timeout_ms / 1000;// 计算秒数tv.tv_usec = (timeout_ms % 1000) * 1000; // 计算微秒数// 设置socket接收超时选项setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));//setsockopt: 设置socket选项,SO_RCVTIMEO表示接收超时}char buffer[1024];//创建1024字节的缓冲区用于存储接收到的数据socklen_t addr_len = sizeof(sender_addr);//获取sender_addr结构体的大小// ------------接收数据--------------ssize_t recv_bytes = recvfrom(sockfd,           // socket描述符buffer,            // 数据存储缓冲区sizeof(buffer) - 1, // 最大接收字节数(留1字节给'\0')0,                 // 标志位(通常为0)(struct sockaddr*)&sender_addr, // 输出参数,接收发送方地址&addr_len);        // 输入时是缓冲区大小,输出时是实际地址长度//错误处理if (recv_bytes < 0) {//接收失败if (errno == EAGAIN || errno == EWOULDBLOCK) {//超时错误(非阻塞模式或设置了超时)throw std::runtime_error("Receive timeout");}throw std::runtime_error(std::string("Receive failed: ") + strerror(errno));}buffer[recv_bytes] = '\0';//最后一位添加\0return std::string(buffer, recv_bytes);//构造std::string: 使用实际接收的字节数创建字符串}// 获取socket描述符int getSocket() const { return sockfd; }// 检查是否初始化bool isInitialized() const { return is_initialized; }
};#endif // UDP_BASE_H

udp_server.h

#ifndef UDP_SERVER_H
#define UDP_SERVER_H#include "udp_base.h"
#include <functional>//服务端需要长时间运行监听
//服务端必须绑定端口,通常不绑定IP(监听所有IP)
class UDPServer : public UDPBase {// 继承UDPBase
private:int port;//std::function智能的函数包装器//const std::string& - 接收到的消息内容(常量引用,避免拷贝);struct sockaddr_in& - 客户端地址信息(引用,可修改)std::function<void(const std::string&, struct sockaddr_in&)> message_callback;public:UDPServer(int port) : port(port) {} // 构造函数,初始化端口// 设置消息回调函数//std::function 是一个通用的函数包装器,它可以存储、复制和调用任何可调用对象void setMessageCallback(std::function<void(const std::string&, struct sockaddr_in&)> callback) {message_callback = callback;//将函数储存为一个对象(创建一个函数包装器,将用户提供的可调用对象存储在其中)}// 启动服务器bool start() {// 1. 初始化socketif (!initialize()) {return false;}// 2. 设置服务器地址结构address.sin_family = AF_INET;      // IPv4协议族address.sin_addr.s_addr = INADDR_ANY; // 监听所有网络接口(0.0.0.0)address.sin_port = htons(port);    // 端口号(主机字节序转网络字节序)// 3. 绑定端口if (bind(sockfd, (struct sockaddr*)&address, sizeof(address)) < 0) {std::cerr << "Bind failed: " << strerror(errno) << std::endl;return false;}std::cout << "UDP Server started on port " << port << std::endl;return true;}// 运行服务器(阻塞式)void run() {if (!is_initialized) {throw std::runtime_error("Server not started");}while (true) {try {struct sockaddr_in client_addr;//局部引用地址,在receiveData被赋值std::string message = receiveData(client_addr);//接收数据std::cout << "Received from " << inet_ntoa(client_addr.sin_addr) //接收到数据的IP<< ":" << ntohs(client_addr.sin_port) << " - " //接收到数据的端口<< message << std::endl;// 如果有回调函数,调用它if (message_callback) {message_callback(message, client_addr);// 调用用户自定义处理} else {// 默认响应std::string response = "Echo: " + message;sendData(response, client_addr);}} catch (const std::exception& e) {std::cerr << "Error: " << e.what() << std::endl;}}}// 发送响应bool sendResponse(const std::string& response, const struct sockaddr_in& client_addr) {return sendData(response, client_addr);}
};#endif // UDP_SERVER_H

udp_client.h

#ifndef UDP_CLIENT_H
#define UDP_CLIENT_H#include "udp_base.h"//客户端按需启动(发)
//客户端必须绑定IP(指向服务端),端口自动分配
class UDPClient : public UDPBase {// 继承UDPBase
private:struct sockaddr_in server_addr; // 存储服务器地址信息public:// 构造函数:初始化服务器地址UDPClient(const std::string& server_ip, int server_port) {server_addr.sin_family = AF_INET;  // IPv4协议族server_addr.sin_port = htons(server_port);// 端口号(主机字节序转网络字节序)// 将字符串IP地址转换为二进制格式if (inet_pton(AF_INET, server_ip.c_str(), &server_addr.sin_addr) <= 0) {throw std::runtime_error("Invalid server address");// IP地址格式错误}}// 连接到服务器(UDP是无连接的,这里只是初始化)bool connect() {return initialize();// 调用基类的initialize()方法创建socket}// 发送消息到服务器bool sendMessage(const std::string& message) {if (!is_initialized) {if (!connect()) {return false;}}return sendData(message, server_addr);//base 的发送数据}// 接收服务器响应std::string receiveResponse(int timeout_ms = 5000) {// 默认5秒超时if (!is_initialized) { // 检查socket是否已初始化throw std::runtime_error("Client not connected");}struct sockaddr_in response_addr;// 用于存储响应来源地址(虽然不需要)return receiveData(response_addr, timeout_ms);//接收数据}// 发送并等待响应std::string sendAndReceive(const std::string& message, int timeout_ms = 5000) {if (!sendMessage(message)) {// 先发送消息throw std::runtime_error("Failed to send message");}return receiveResponse(timeout_ms);// 然后等待接收响应}
};#endif // UDP_CLIENT_H

main.cpp

#include "UDP/udp_base.h"
#include "UDP/udp_client.h"
#include "UDP/udp_server.h"
UDPServer udpserver(8080);//UDP通信端口
// 自定义消息处理回调函数
//const std::string& message: 接收到的消息内容(只读引用,避免拷贝)
//struct sockaddr_in& client_addr: 客户端地址信息(可修改引用)
void customMessageHandler(const std::string& message, struct sockaddr_in& client_addr) {std::cout << "Custom handler processing: " << message << std::endl;receive_message=message;if(receive_message=="1"){std::string send_message="2";udpserver.sendResponse(send_message,client_addr);}// 这里可以添加自定义处理逻辑
}
void runServer() {// UDPServer udpserver(8080);//UDP通信端口udpserver.setMessageCallback(customMessageHandler);//setMessageCallback将用户函数保存到成员变量中if (udpserver.start()) {udpserver.run(); // 这会阻塞,所以在单独线程中运行}
}
void runClient() {std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "启动UDP客户端..." << std::endl;UDPClient client("127.0.0.1", 8080);  // 连接到本地服务器的8080端口try {std::cout << "客户端连接到: 127.0.0.1:8080" << std::endl;std::string response = client.sendAndReceive("Hello Server!");std::cout << "服务器响应: " << response << std::endl;} catch (const std::exception& e) {std::cerr << "客户端错误: " << e.what() << std::endl;}
}
int main(int argc, char **argv)
{
// 在单独线程中运行udp服务器std::thread server_thread(runServer);NR_INFO("UDP Start success");
}

测试:

以串口工具作为客户端,以板卡程序作为服务端,监听客户端发送数据,若监听到客户端发送为1,则板卡服务端使用回调函数向串口工具发送2


文章转载自:

http://4PEplt9S.zmrbq.cn
http://ZBNcjD4K.zmrbq.cn
http://wNkbEDI4.zmrbq.cn
http://qEOd3PG0.zmrbq.cn
http://wFgDPBsV.zmrbq.cn
http://ghYmrXGg.zmrbq.cn
http://dMidvbNp.zmrbq.cn
http://SIFMgAtZ.zmrbq.cn
http://He9MBH0F.zmrbq.cn
http://RW1Jb8ed.zmrbq.cn
http://zMcPnOtL.zmrbq.cn
http://MaOhr35J.zmrbq.cn
http://8e9lDh1D.zmrbq.cn
http://iAiFdNF5.zmrbq.cn
http://aRzO49ap.zmrbq.cn
http://wJTngeIu.zmrbq.cn
http://IpAy1foi.zmrbq.cn
http://Q6TVi4p6.zmrbq.cn
http://mK1WdOcR.zmrbq.cn
http://JhdVbMMO.zmrbq.cn
http://gtpIMhSB.zmrbq.cn
http://ndsHNBVa.zmrbq.cn
http://2LGOKSIl.zmrbq.cn
http://GXHNEvoe.zmrbq.cn
http://bJ4p5tIV.zmrbq.cn
http://PaTKiXnG.zmrbq.cn
http://8E8LfsAW.zmrbq.cn
http://JnUujTOD.zmrbq.cn
http://rfxF83Bg.zmrbq.cn
http://RavdPpwp.zmrbq.cn
http://www.dtcms.com/a/372723.html

相关文章:

  • WGAI项目图像视频语音识别功能
  • 9.3深度循环神经网络
  • 【嵌入式硬件实例】-555定时器实现自动晚灯(220V)
  • Linux Shell | set、env、export 用法区别
  • 浅聊一下微服务的服务保护
  • Nginx 实战系列(五)—— Nginx流量监控:从stub_status到nginx-module-vts的进阶指南
  • 34. 什么是反射
  • YOLO11 改进、魔改|通道自注意力卷积块CSA-ConvBlock,实现 “轻量化特征增强”
  • 优先搜索(DFS)实战
  • 计算机视觉opencv----银行卡号码识别
  • 第六章、从transformer到nlp大模型:编码器-解码器模型 (Encoder-Decoder)
  • pymodbus启动一个简单的modbus tcp server
  • 【NowCoder】牛客周赛 Round 108 EF (背包问题 | SOSDP)
  • 【ARMday02】
  • OFDR设备开机到出图的5个关键操作步骤
  • ArcGIS学习-19 实战-表面分析
  • 【算法】双指针(二)复写零
  • 视频串行解串器(SerDes)介绍
  • PyTorch 动态图的灵活性与实用技巧
  • 【P01_AI测试开发课程-导论】
  • 从社交破冰到学习规划,鸿蒙5开启智慧校园新生活
  • 【Linux操作系统】简学深悟启示录:文件fd
  • Kata Container 部署与应用实践
  • 【CentOS7】docker安装成功后测试,报Unable to find image ‘hello-world:latest‘ locally
  • springboot配置请求日志
  • 2-ATSAMV71Q21-BOOT
  • 【Qt开发】显示类控件(一)-> QLabel
  • 把不确定变成确定性收益:电力交易未来场景的预测、优化与实操
  • 大数据毕业设计选题推荐-基于大数据的国家药品采集药品数据可视化分析系统-Spark-Hadoop-Bigdata
  • 如何在Linux上使用Docker在本地部署开源PDF工具Stirling PDF:StirlingPDF+cpolar让专业操作像在线文档一样简单