PMTUD By UDP

从一次线上故障说起:为什么UDP视频流会卡顿?聊聊MTU、PMTUD和巨型帧(Jumbo Frame)的实战选择 本文通过一次UDP视频流卡顿的线上故障案例,深入分析了MTU(最大传输单元)对网络性能的影响。探讨了PMTUD机制在实际环境中的局限性,并提出了包括调整MTU大小、启用巨型帧(Jumbo Frame)等实战优化策略,帮助解决视频流传输中的卡顿问题。 阅读详情
通过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     8

int 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     8

int 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; // 500ms
    if (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 range
                continue;
            }

            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) {
            // timeout
            if (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);

    // 探测路径MTU
    int 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;
}
UDP分片与丢包:嵌入式系统中不可靠传输的工程实践 UDP是一种面向消息、无连接的传输层协议,其核心特性是轻量与低延迟,但不提供可靠性保障。其行为受IP层MTU限制,当数据超过路径MTU时触发IP分片,而任一分片丢失即导致整包丢弃;同时,UDP接收缓冲区有界,溢出后静默丢包,成为嵌入式系统中丢包的主因之一。理解UDP报文结构、校验和机制、内核缓冲区模型及分片逻辑,是构建稳定网络通信的基础。在MCU、WiFi模组、4G终端等资源受限场景下,需结合MTU探测、接收队列调优、RUDP增强或FEC冗余等手段,将UDP从‘尽最大努力交付’提升为‘可预期交付’。本文聚焦 阅读详情

相关推荐

TCP与UDP的核心差异与性能优化实战

传输层协议是网络通信的基石,TCP和UDP作为最常用的两种协议,在设计哲学和实现机制上存在本质差异。TCP通过三次握手、确认应答、流量控制等复杂机制确保可靠传输,适合文件传输等场景;UDP则采用极简设计,牺牲可靠性换取传输效率,广泛应用于实时视频、在线游戏等低延迟需求场景。理解两者的内核实现原理(如TCP的重传定时器、拥塞控制算法,UDP的MTU发现机制)对网络性能调优至关重要。通过Wireshark抓包分析和Linux内核参数调优,可以显著提升TCP高并发处理能力和UDP传输稳定性。掌握这些核心知识,能够

weixin_30856725的博客 307

Linux 网络:PMTUD 协议简介

Linux,网络,PMTUD 协议

JiMoKuangXiangQu的专栏 2716

MTU 1500这个数字是怎么来的?从以太网帧到UDP有效载荷,一步步算给你看

本文详细解析了MTU 1500这一网络标准数字的由来,从以太网帧结构到UDP有效载荷的计算过程,逐步拆解其背后的数学逻辑。通过分析IP分包和UDP传输特性,揭示了数据包大小限制的原因,并探讨了巨型帧技术及MTU问题排查方法,帮助读者深入理解网络协议栈中的数据包处理机制。

weixin_42614269的博客 433

TCP/IP卷一:58---UDP之(路径MTU发现机制(PMTUD)、UDP最大数据报长度(数据报截断)、UDP/IPv4和UDP/IPv6数据报的转换)

一、采用UDP的路径MTU发现 MTU介绍见文章:https://blog.youkuaiyun.com/qq_41453285/article/details/95997936 UDP的路径MTU发现 让我们考察使用UDP的应用程序与路径MTU发现机制(PMTUD)之间的交互过程 对一个像UDP这样的协议来说,调用这样协议的应用程序一般只控制输出数据报的大小,如果有方法能确定一个可以避免分片的合适的...

董哥的黑板报 2096

UDP大数据传输:IP层分片还是应用层切块?

UDP协议不会拆分或合并数据,而是直接将应用层数据原封不动加上头部交给IP层。当数据超过网络MTU时,IP层会自动分片传输,但分片容易丢包且不可靠。理论上UDP数据报最大长度65535字节,实际应用中建议不超过1472字节(以太网MTU限制)。可靠的做法是在应用层自行分片、编号和重发,而非依赖IP分片。总结:UDP不处理数据分合,大数据传输应由应用层控制以确保可靠性。

本博客聚焦游戏开发技巧、创业实战心得及大学热门课程干货。这里既有技术深度,也有思维广度,无论你是开发者、创业者还是高校学子,都能在这里找到适合自己的成长之路! 483

水文模型大合集---SWAT模型、VIC模型、HEC-HMS模型、HSPF模型、FVCOM模型、Delft3D模型、HYPE模型.......

1.1 面源污染概要1.2 SWAT模型及应用1.3 SWAT模型原理1.4 SWAT模型输入文件1.5 ArcGIS与SWAT关系。

jwwkyjspt的博客 4359

山石防火墙mtu值设置_IPv4 Fragmentation, MTU, MSS 和 PMTUD

IPv4 Fragmentation 存在的意义IPv4 设计的最大容量是 65535 bytes (包括 header),然而网络当中有多种不同的传输链路且每种链路所对应的最大传输值(MTU)都不同。为了适应不同的链路,IPv4 在设计时就允许各网络节点对数据包进行 data fragmentation。比如一台 router 从 FDDI 接口收到了一个大小为 4352 bytes 的数据包,...

weixin_30773813的博客 1273

PX4与ROS协同仿真:从键盘控制到无人机自主飞行

本文详细介绍了如何搭建PX4与ROS协同仿真环境,实现从键盘控制到无人机自主飞行的完整流程。通过XTDrone项目整合PX4飞控、ROS通信框架与Gazebo物理引擎,逐步讲解环境配置、键盘控制实现原理,并深入剖析了迈向Offboard模式实现自主飞行的核心代码与数据流,为无人机算法开发与测试提供了安全的仿真沙盒。

weixin_29169899的博客 498

MSS PMTUD的问题

在大多数操作系统中都默认开启了PMTUD功能,在发送数据报的时候都设置了DF位,并由此来发现path的mtu,但是由于网络上的种种原因导致PMTUD检测失败出现数据包不能被分片,应用层服务不能正常访问。当两台远程PC互联的时候,它们的数据需要穿过很多的路由器和各种各样的网络媒介才能到达对端,网络中不同媒介的MTU各不相同,就好比一长段的水管,由不同粗细的水管组成(MTU不同 ...

weixin_34088583的博客 435

告别命令行恐惧!用TBtools的MCScanX插件,5分钟搞定拟南芥和水稻的基因组共线性分析

本文介绍如何利用TBtools的MCScanX插件快速完成拟南芥和水稻的基因组共线性分析,无需命令行操作,5分钟即可生成专业可视化结果。通过图形化界面简化传统MCScanX的复杂流程,特别适合非生物信息背景的研究者,显著提升基因组数据分析效率。

dianzhuique8587的博客 377

告别Link180!在Ansys Mechanical 2020R2中正确启用Cable280绳索单元(附Beta选项开启指南)

本文详细介绍了在Ansys Mechanical 2020R2中启用Cable280绳索单元的全过程,包括Beta选项的开启步骤、建模技巧及高阶应用。Cable280作为专为绳索仿真设计的单元,相比传统的Link180单元,在大变形模拟和计算效率上表现更优,特别适合斜拉桥、电梯牵引绳等工程场景。

weixin_33711647的博客 405

【MTU】关于MTU size| MTU size, PMTUD 和分段

最大传输单元(MTU)是单个数据报在特定数据通信链路上可以具有的最大字节数。使用封装,加密或覆盖网络协议会导致端到端有效MTU Size会减小(增加协议头占用有效载荷?)。某些应用程序在减小的MTU Size上可能无法很好地工作,并且无法执行路径MTU发现(PMTUD)。作为应对,能够增加网络链路的MTU Size将是很好的。 MTU Size 最大传输单元(MTU)是OSI模型2层网络数据单元(PDU)的最大可能帧大小。大小取决于通信媒体的物理属性。历史网络媒体速度较慢,更容易出错,因此MTU .

我的笔记本 3155

一文搞懂深度学习:最全神经网络介绍

本文是深度学习系列文章的第二篇,我们将深入探讨各种类型的人工神经网络,探索它们独特的特性和应用。

2402_82802238的博客 2800

从一次线上告警说起:UDP大报文传输的坑,我们是怎么填上的?

本文通过一次线上告警案例,深入分析了UDP大报文传输中的分片问题及其解决方案。文章详细探讨了MTU限制、IP层分片风险,并提出了应用层手动分片、调整系统MTU和启用PMTUD等多种应对策略,最终采用混合方案显著降低了丢包率。

weixin_42531925的博客 329

基于Simulink的防空导弹六自由度弹道建模与仿真实战

本文详细介绍了基于Simulink的防空导弹六自由度弹道建模与仿真方法。通过分析导弹动力学原理、Simulink环境配置、气动力建模、推进与控制系统设计等关键环节,提供了一套完整的实战指南。文章特别强调了六自由度模型在防空导弹仿真中的重要性,并分享了常见问题排查与优化建议,为相关领域工程师提供实用参考。

apple5的专栏 868

通讯软件013——分分钟学会Kepware OPC AE Server仿真配置

本文介绍如何使用Kepware软件仿真OPC AE Server配置。

zhouwl72的博客 2227

UDP协议

1、UDP用户数据报协议 UDP是一种保留消息边界的简单的面向数据报的、无连接的、无序的快速灵活的传输层协议,它不提供队列管理,重复消除,流量控制,拥塞控制等 1、UDP数据报格式 UDP长度(lenght):当UDP封装在IPv6中时,如果使用巨型负载,则UDP数据报的大小可以超过65535个字 节,UDP头中的长度位数只有2个字节不够时会被设置为0,虚拟头部的长度为32位

蔷薇_wendy的博客 1192

Vue - 最新详细实现滑块拖动拼图验证,网站登录验证滑块验证功能,拖动滑块到图片缺口的位置即为成功,滑块验证安全检测(详细示例代码,一键复制运行)

您一定见过很多系统都“配备”了滑块验证、拼图验证、找不同等,本质上都是为了验证登录系统的是人还是机器(代码),如下图所示效果: 您打开项目根目录,终端执行如下命令:拼图式拖动滑块验证 验证拖动滑块验证,滑动拼图/图片点选验证 接下来,在中加入以下代码: 如下图所示: 更多配置示例和参数详见官方文档,以下便是如上效果图的代码:.........

🏆 前端领域优质创作者 1万+

TCP/IP卷1学习: UDP-Lite 详解:部分校验和的轻量UDP协议

替换字段:UDP-Lite 将 UDP 头部的 Length 字段替换为 Checksum Coverage 字段,头部大小不变,仍为 8 字节。覆盖规则Coverage0Coverage0:覆盖整个数据报(与普通 UDP 行为相同)1≤Coverage≤71≤Coverage≤7:非法Coverage≥8Coverage≥8:覆盖从头部起的前 Coverage 个字节独立协议:协议号 136,与 UDP(17)完全独立,防火墙和路由器可区分处理。应用场景。

虾球 425

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

UDP与IP分片

一、UDP 1. UDP特点 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUf1OSBA-1642486941260)(r5ub5agkh.hb-bkt.clouddn.com/pic/1.jpg)] 它不提供差错纠正、队列管理、重复消除、流量控制和拥塞控制。它提供差错检测,包含我们在传输层中碰到的第一个真实的端到端(end-to-end)校验和。 我们可能考虑到这种可靠性和保护性的缺失,而会一般认为使用UDP一点好处也没有。但是,这是不对的。因为它的无连接特征,它要比其

Czuaphe的博客 5253

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

UDP的数据包大小的限制问题

分片的触发条件:只有当 单个 UDP 数据包(含 IP 头 + UDP 头 + 用户数据)的总长度 > 路径 MTU 时,IP 层才会将这个 “超大数据包” 拆分成多个 “IP 分片”(这些分片属于同一个原始 IP 数据报,共享同一个 IP 标识字段)。(UDP其实没有重发选项)分片会增加网络延迟、带宽消耗以及丢包风险,从而影响性能。超过 MTU:仅单个超大数据包会拆分成多个分片(同属一个数据报),下一次新数据无论是否超 MTU,都重新开新的 IP 数据报(或新的分片组),不续写之前的分片。

2501_90717618的博客 1337

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

MTU 1500 字节下,TCP 与 UDP 分片行为 3 点关键差异对比

本文深度解析了在MTU 1500字节网络环境下TCP与UDP的分片行为差异。TCP通过MSS协商主动避免IP分片,而UDP则依赖IP层被动分片,导致重组可靠性和网络性能显著不同。文章通过对比表格和代码示例,揭示了两种协议在分片触发点、控制方和重组机制等维度的关键区别,并提供了实用的分片问题排查命令。

weixin_30443731的博客 474

从网络编程到性能调优:深入理解MTU与TCP/UDP数据包设计

本文深入探讨了MTU(最大传输单元)在网络编程中的关键作用,以及如何通过优化TCP/UDP数据包设计提升性能。通过实际案例和命令行示例,详细解析了MTU对数据传输效率的影响,并提供了包括调整MSS、禁用Nagle算法、使用巨型帧等实用调优技巧,帮助开发者构建更高效的网络应用。

weixin_30522095的博客 315

《TCP/IP详解 卷一》第10章 UDP 和 IP 分片

UDP是简单协议。需要组播广播时使用UDP,可避免连接开销。UDP使用场景:多媒体,P2P。

qingwangheni的博客 2344

UDP包的大小与MTU

在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好? 当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,我这里仅对 像ICQ一类的发送聊天消息的情况作分析,对于其他情况,你或许也能得到一点帮助: 首先,我们知道,TCP/IP通常被认为是一个四层协议系统,包括链路层,网络层,运输层,应用层. UDP属于运输层,下面我们由下至上一步一步来看:

zqf_office的专栏 635
上一篇: C++替代标识符
下一篇: ICMP Fragmentation needed抓包实例
山有木兮啊
博客等级 码龄8年 135粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值