qidao123.com技术社区-IT企服评测·应用市场

标题: 【Linux网络#3】:Socket编程应用层UDP [打印本页]

作者: 络腮胡菲菲    时间: 2025-5-6 05:02
标题: 【Linux网络#3】:Socket编程应用层UDP
1.媒介

UDP(User Datagram Protocol)是面向无毗连的不可靠数据报传输协议,其特点如下:

典范应用场景:

2.代码框架计划

项目结构

  1. udp_project/
  2. ├── include/
  3. │   ├── UdpServer.hpp     // 服务器核心类
  4. │   ├── Common.hpp        // 公共定义
  5. │   └── Log.hpp           // 日志系统
  6. ├── src/
  7. │   ├── UdpServerMain.cc  // 服务器入口
  8. │   └── UdpClientMain.cc  // 客户端入口
  9. └── Makefile              // 编译脚本
复制代码
3.基本实现 - EchoServer

UdpServer.hpp 核心实现

  1. class UdpServer
  2. {
  3. public:
  4.     UdpServer(const std::string &ip = gdefaultip, uint16_t port = gdefaultport)
  5.         : _sockfd(gsockfd), _ip(ip), _port(port), _isrunning(false) {}
  6.     void InitServer()
  7.     {
  8.         // 创建套接字
  9.         _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  10.         if (_sockfd < 0) {
  11.             LOG(FATAL) << "Socket creation failed: " << strerror(errno);
  12.             Die(SOCKET_ERR);
  13.         }
  14.         LOG(INFO) << "Socket created successfully, FD: " << _sockfd;
  15.         // 绑定地址
  16.         sockaddr_in local;
  17.         bzero(&local, sizeof(local));
  18.         local.sin_family = AF_INET;
  19.         local.sin_port = htons(_port);
  20.         local.sin_addr.s_addr = inet_addr(_ip.c_str());
  21.         if (bind(_sockfd, CONV(&local), sizeof(local)) < 0) {
  22.             LOG(FATAL) << "Binding failed: " << strerror(errno);
  23.             Die(BIND_ERR);
  24.         }
  25.         LOG(INFO) << "Binding successful";
  26.     }
  27.     void Start()
  28.     {
  29.         _isrunning = true;
  30.         while (true) {
  31.             char inbuffer[1024];
  32.             sockaddr_in peer;
  33.             socklen_t len = sizeof(peer);
  34.             
  35.             // 接收数据
  36.             ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer)-1, 0,
  37.                                 CONV(&peer), &len);
  38.             if (n > 0) {
  39.                 inbuffer[n] = '\0';
  40.                 LOG(DEBUG) << "Client says: " << inbuffer;
  41.                
  42.                 // 构建响应
  43.                 std::string response = "Echo from server: ";
  44.                 response += inbuffer;
  45.                
  46.                 // 发送响应
  47.                 sendto(_sockfd, response.c_str(), response.size(), 0,
  48.                       CONV(&peer), sizeof(peer));
  49.             }
  50.         }
  51.     }
  52. private:
  53.     int _sockfd;
  54.     uint16_t _port;
  55.     std::string _ip;
  56.     bool _isrunning;
  57. };
复制代码
代码片断分析

InitServer() 方法详解

  1. void InitServer()
  2. {
  3.     // 1. 创建套接字
  4.     _sockfd = ::socket(AF_INET, SOCK_DGRAM, 0);
  5.     if(_sockfd < 0) {
  6.         // 错误处理:使用日志系统记录错误信息
  7.         LOG(LogLevel::FATAL) << "socket: " << strerror(errno);
  8.         Die(SOCKET_ERR);
  9.     }
  10.    
  11.     // 2. 填充地址结构
  12.     struct sockaddr_in local;
  13.     bzero(&local, sizeof(local)); // 清空结构体
  14.     local.sin_family = AF_INET;  // IPv4协议
  15.     local.sin_port = htons(_port); // 主机字节序转网络字节序
  16.     local.sin_addr.s_addr = inet_addr(_ip.c_str()); // IP地址转换
  17.    
  18.     // 3. 绑定套接字
  19.     int n = ::bind(_sockfd, CONV(&local), sizeof(local));
  20.     if(n < 0) {
  21.         LOG(LogLevel::FATAL) << "bind: " << strerror(errno);
  22.         Die(BIND_ERR);
  23.     }
  24. }
复制代码
关键点分析:
    sockaddr_in结构体
  1. struct sockaddr_in {
  2.     sa_family_t    sin_family; // 地址族
  3.     in_port_t      sin_port;   // 端口号
  4.     struct in_addr sin_addr;   // IPv4地址
  5.     char           sin_zero[8]; // 填充字段
  6. };
复制代码
Start() 方法详解

  1. void Start()
  2. {
  3.     _isrunning = true;
  4.     while(true) {
  5.         char inbuffer[1024];
  6.         struct sockaddr_in peer;
  7.         socklen_t len = sizeof(peer);
  8.         
  9.         // 接收数据报
  10.         ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer)-1, 0,
  11.                             CONV(&peer), &len);
  12.         
  13.         if(n > 0) {
  14.             inbuffer[n] = '\0'; // 添加字符串结束符
  15.             
  16.             // 构建响应数据
  17.             std::string echo_string = "echo# ";
  18.             echo_string += inbuffer;
  19.             
  20.             // 发送响应
  21.             sendto(_sockfd, echo_string.c_str(), echo_string.size(), 0,
  22.                   CONV(&peer), sizeof(peer));
  23.         }
  24.     }
  25. }
复制代码
核心API分析:

1.recvfrom()
  1. ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
  2.                 struct sockaddr *src_addr, socklen_t *addrlen);
复制代码

2.sendto() :
  1. ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
  2.               const struct sockaddr *dest_addr, socklen_t addrlen);
复制代码

3.UdpClientMain.cc 客户端实现

  1. #include "UdpClient.hpp"
  2. #include <iostream>
  3. #include <cstring>
  4. #include <string>
  5. #include <cstdlib>
  6. #include <sys/socket.h>
  7. #include <netinet/in.h>
  8. #include <arpa/inet.h>
  9. #include "Common.hpp"
  10. int main(int argc, char *argv[])
  11. {
  12.     if(argc != 3) {
  13.         std::cerr << "Usage: " << argv[0] << " serverIp serverPort" << std::endl;
  14.         Die(USAGE_ERR);
  15.     }
  16.     std::string serverip = argv[1];
  17.     uint16_t serverport = std::stoi(argv[2]);
  18.     // 创建套接字
  19.     int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
  20.     if(sockfd < 0) {
  21.         std::cerr << "Socket creation failed: " << strerror(errno) << std::endl;
  22.         Die(SOCKET_ERR);
  23.     }
  24.     // 填充服务器地址
  25.     sockaddr_in server;
  26.     memset(&server, 0, sizeof(server));
  27.     server.sin_family = AF_INET;
  28.     server.sin_port = htons(serverport);
  29.     server.sin_addr.s_addr = inet_addr(serverip.c_str());
  30.     // 通信循环
  31.     while(true) {
  32.         std::cout << "Client> ";
  33.         std::string message;
  34.         std::getline(std::cin, message);
  35.         // 发送请求
  36.         sendto(sockfd, message.c_str(), message.size(), 0,
  37.               CONV(&server), sizeof(server));
  38.         // 接收响应
  39.         char buffer[64];
  40.         sockaddr_in tmp;
  41.         socklen_t len = sizeof(tmp);
  42.         ssize_t n = recvfrom(sockfd, buffer, sizeof(buffer)-1, 0,
  43.                             CONV(&tmp), &len);
  44.         
  45.         if(n > 0) {
  46.             buffer[n] = '\0';
  47.             std::cout << "Server> " << buffer << std::endl;
  48.         }
  49.     }
  50.     return 0;
  51. }
复制代码
注意事项

地址转换技巧
  1. // 字符串IP转网络序整数
  2. uint32_t ip = inet_addr("192.168.1.1");
  3. // 网络序整数转字符串
  4. char ip_str[INET_ADDRSTRLEN];
  5. inet_ntop(AF_INET, &ip, ip_str, INET_ADDRSTRLEN);
复制代码
4.UdpServer.hpp 修改 - 打印客户端信息

  1. void Start()
  2. {
  3.     _isrunning = true;
  4.     while (true) {
  5.         char inbuffer[1024];
  6.         sockaddr_in peer;
  7.         socklen_t len = sizeof(peer);
  8.         
  9.         ssize_t n = recvfrom(_sockfd, inbuffer, sizeof(inbuffer)-1, 0,
  10.                             CONV(&peer), &len);
  11.         
  12.         if (n > 0) {
  13.             inbuffer[n] = '\0';
  14.             
  15.             // 获取客户端信息
  16.             std::string client_ip = inet_ntoa(peer.sin_addr);
  17.             uint16_t client_port = ntohs(peer.sin_port);
  18.             
  19.             LOG(DEBUG) << "Client[" << client_ip << ":" << client_port
  20.                        << "] says: " << inbuffer;
  21.             
  22.             // 构建响应
  23.             std::string response = "Echo from server: ";
  24.             response += inbuffer;
  25.             
  26.             sendto(_sockfd, response.c_str(), response.size(), 0,
  27.                   CONV(&peer), sizeof(peer));
  28.         }
  29.     }
  30. }
复制代码
5.通讯验证(Linux-Linux)

启动服务器

  1. # 编译服务器
  2. g++ -o server UdpServerMain.cc -Iinclude
  3. # 运行服务器
  4. ./server 127.0.0.1 8080
复制代码
启动客户端

  1. # 编译客户端
  2. g++ -o client UdpClientMain.cc -Iinclude
  3. # 运行客户端
  4. ./client 127.0.0.1 8080
复制代码
交互示例

服务器端日志
  1. [INFO] Socket created successfully, FD: 3
  2. [INFO] Binding successful
  3. [DEBUG] Client[127.0.0.1:34567] says: Hello Server
  4. [DEBUG] Client[127.0.0.1:34567] says: UDP Test
复制代码
客户端交互:
  1. Client> Hello Server
  2. Server> Echo from server: Hello Server
  3. Client> UDP Test
  4. Server> Echo from server: UDP Test
复制代码
6.核心API总结



7.错误处理机制

错误码定义(Common.hpp)
  1. enum ErrorType {
  2.     USAGE_ERR = 1,
  3.     SOCKET_ERR,
  4.     BIND_ERR,
  5.     // ...其他错误码
  6. };
复制代码
日志体系(Log.hpp)
  1. namespace LogModule {
  2.     enum LogLevel {
  3.         DEBUG = 0,
  4.         INFO,
  5.         WARNING,
  6.         ERROR,
  7.         FATAL
  8.     };
  9. }
复制代码
























免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/) Powered by Discuz! X3.4