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

PMTUD By UDP

通过UDP探测MTU,并实现udp echo server
// Description: UDP echo server.
// g++ udp_echo_server.cc -o udp_echo_server
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>#define PORT                12345
#define BUFFER_SIZE         4096
#define IPV4_HEADER_SIZE    20
#define UDP_HEADER_SIZE     8int main() {int sockfd;char buffer[BUFFER_SIZE];struct sockaddr_in servaddr, cliaddr;socklen_t len;// 创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {std::cerr << "Socket creation failed" << std::endl;return 1;}// 设置不分片选项int dontfrag = IP_PMTUDISC_DO;if (setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &dontfrag, sizeof(dontfrag)) < 0) {std::cerr << "Failed to set IP_MTU_DISCOVER option" << std::endl;close(sockfd);return 1;}// 清空地址结构memset(&servaddr, 0, sizeof(servaddr));memset(&cliaddr, 0, sizeof(cliaddr));// 填充服务器信息servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(PORT);// 绑定套接字到指定端口if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {std::cerr << "Bind failed" << std::endl;close(sockfd);return 1;}std::cout << "UDP echo server started, listening on port " << PORT << "..." << std::endl;while (true) {len = sizeof(cliaddr);// 接收数据int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&cliaddr, &len);if (n > 0) {buffer[n] = '\0';char host_buffer[INET_ADDRSTRLEN];inet_ntop(AF_INET, &cliaddr.sin_addr, host_buffer, INET_ADDRSTRLEN);printf("RX from [%s:%u] -> %d B, mtu = %d\n", host_buffer, ntohs(cliaddr.sin_port), n, n + IPV4_HEADER_SIZE + UDP_HEADER_SIZE);// 发送回显数据sendto(sockfd, buffer, n, 0, (const struct sockaddr *)&cliaddr, len);} else {perror("recvfrom");}}close(sockfd);return 0;
}
// Description: UDP echo client with path MTU discovery.
// g++ udp_echo_client.cc -o udp_echo_client
#include <iostream>
#include <cstring>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>#define SERVER_PORT 12345
#define BUFFER_SIZE 65536#define IPV4_HEADER_SIZE    20
#define UDP_HEADER_SIZE     8int discover_path_mtu(const struct sockaddr_in &servaddr)
{int sockfd;char probe_packet[BUFFER_SIZE];int32_t mtu_lbound, mtu_current, mtu_ubound, mtu_best;mtu_best = 68;mtu_lbound = 68;mtu_ubound = 65535;struct sockaddr_in remote_host;// 创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {std::cerr << "Socket creation failed" << std::endl;return -1;}// 设置不分片选项int dontfrag = IP_PMTUDISC_DO;if (setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &dontfrag, sizeof(dontfrag)) < 0) {std::cerr << "Failed to set IP_MTU_DISCOVER option" << std::endl;close(sockfd);return -1;}struct timeval timeout;timeout.tv_sec = 0;timeout.tv_usec = 500000; // 500msif (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(timeout)) != 0) {perror("Error in setsockopt(timeout/udp)");return -1;}while (mtu_lbound <= mtu_ubound) {mtu_current = (mtu_lbound + mtu_ubound) / 2;memset(probe_packet, 'a', mtu_current - IPV4_HEADER_SIZE - UDP_HEADER_SIZE); // 减去IPv4头(20字节)和UDP头(8字节)probe_packet[mtu_current - IPV4_HEADER_SIZE - UDP_HEADER_SIZE] = '\0';// 发送探测数据包int bytes = sendto(sockfd, probe_packet, mtu_current - IPV4_HEADER_SIZE - UDP_HEADER_SIZE, 0, (const struct sockaddr *)&servaddr, sizeof(servaddr));if (bytes < 0) {if (errno == EMSGSIZE) {printf("packet(%d) [%d, %d] too big for local interface\n", mtu_current, mtu_lbound, mtu_ubound);mtu_ubound = mtu_current - 1; // update rangecontinue;}perror("Error in sendto()");return -1;}char buffer[BUFFER_SIZE] = {0};socklen_t len = sizeof(remote_host);bytes = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&remote_host, &len);if (bytes < 0) {// timeoutif (errno == EAGAIN || errno == EWOULDBLOCK) {printf("timeout\n");mtu_ubound = mtu_current - 1;continue;}perror("Error in recvfrom()");return -1;} else if (bytes > 0) {printf("mtu = %d [%d, %d]\n", bytes + IPV4_HEADER_SIZE + UDP_HEADER_SIZE, mtu_lbound, mtu_ubound);if (strncmp(probe_packet, buffer, bytes) == 0) {mtu_lbound = mtu_current + 1;} else {mtu_ubound = mtu_current - 1;}if (mtu_current > mtu_best) {mtu_best = mtu_current;}} else {printf("recvfrom() returned 0\n");return -1;}}close(sockfd);std::cout << "The MTU of the path is: " << mtu_best << " B" << std::endl;return mtu_best;
}int main(int argc, char* argv[])
{int opt;const char *server_address = "127.0.0.1";int32_t port = SERVER_PORT;while ((opt = getopt(argc, argv, "s:p:")) != -1) {switch (opt) {case 's':server_address = optarg;break;case 'p':port = atoi(optarg);break;default: /* '?' */fprintf(stderr, "Usage: %s -s server_address -p port\n", argv[0]);return 0;}}// 解析服务器地址struct hostent* host = gethostbyname(server_address);if (host == nullptr) {std::cerr << "Failed to resolve hostname: " << server_address << std::endl;return 1;}int sockfd;char buffer[BUFFER_SIZE];struct sockaddr_in servaddr;// 创建UDP套接字sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (sockfd < 0) {std::cerr << "Socket creation failed" << std::endl;return 1;}// 设置不分片选项int dontfrag = IP_PMTUDISC_DO;if (setsockopt(sockfd, IPPROTO_IP, IP_MTU_DISCOVER, &dontfrag, sizeof(dontfrag)) < 0) {std::cerr << "Failed to set IP_MTU_DISCOVER option" << std::endl;close(sockfd);return -1;}// 清空服务器地址结构memset(&servaddr, 0, sizeof(servaddr));// 填充服务器信息servaddr.sin_family = AF_INET;servaddr.sin_port = htons(port);memcpy(&servaddr.sin_addr, host->h_addr, host->h_length);// 探测路径MTUint mtu = discover_path_mtu(servaddr);if (mtu <= 0) {std::cerr << "Path MTU detection failed" << std::endl;}while (true) {std::cout << "Please enter the data to be sent:";std::string message;std::getline(std::cin, message);if (message.empty()) {break;}// 发送数据sendto(sockfd, message.c_str(), message.size(), 0, (const struct sockaddr *)&servaddr, sizeof(servaddr));// 接收回显数据socklen_t len = sizeof(servaddr);int n = recvfrom(sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr *)&servaddr, &len);buffer[n] = '\0';std::cout << "RX:" << buffer << std::endl;}close(sockfd);return 0;
}
http://www.lryc.cn/news/537058.html

相关文章:

  • Hutool - BloomFilter:便捷的布隆过滤器实现
  • 【学习资源】时间序列数据分析方法(1)
  • 盛铂科技SWFA100捷变频频率综合器:高性能国产射频系统的关键选择
  • 释放你的元数据:使用 Elasticsearch 的自查询检索器
  • 【快速幂算法】快速幂算法讲解及C语言实现(递归实现和非递归实现,附代码)
  • 3. 导入官方dashboard
  • 怎么理解 Spring Boot 的约定优于配置 ?
  • Dify 是什么?Dify是一个开源的LLM应用开发平台,支持快速搭建生成式AI应用,具有RAG管道、Agent功能、模型集成等特点
  • 数据预处理都做什么,用什么工具
  • windows蓝牙驱动开发-在蓝牙配置文件驱动程序中接受 L2CAP 连接
  • 【原理图PCB专题】自制汉字转码工具,适配Allgero 17版本 Skill
  • 欧拉公式在信号处理中的魔法:调幅信号的生成与频谱分析
  • 如何在Ubuntu中切换多个PHP版本
  • 基于opencv的HOG+角点匹配教程
  • Linux线程概念与线程操作
  • AI软件栈:LLVM分析(五)
  • Git指南-从入门到精通
  • Linux 文件系统挂载
  • Qt QSpinBox 总结
  • 【OJ项目】深入剖析题目接口控制器:功能、实现与应用
  • 周考考题(学习自用)
  • 【matlab】大小键盘对应的Kbname
  • LabVIEW与小众设备集成
  • Android 系统Service流程
  • Gartner预测2025年网络安全正在进入AI动荡时期:软件供应链和基础设施技术堆栈中毒将占针对企业使用的人工智能恶意攻击的 70% 以上
  • 华为最新OD机试真题-最长子字符串的长度(一)-Python-OD统一考试(E卷)
  • HAL库框架学习总结
  • 基于Spring Integration的ESB与Kettle结合实现实时数据处理技术
  • qt QOpenGLContext详解
  • 探索顶级汽车软件解决方案:驱动行业变革的关键力量