首页 文章

到特定远程ip的第一条UDP消息会丢失

提问于
浏览
8

我正在研究基于LAN的解决方案,其中“服务器”必须控制多个“播放器”我选择的协议是UDP,因为它很容易,我不需要连接,我的流量只包含不时的短命令我想使用混合的广播消息进行同步,单个目标消息用于玩家个人命令 .

多播TCP将是一种替代方案,但它更复杂,不完全适合任务,并且通常不受硬件的良好支持 .

不幸的是我遇到了一个奇怪的问题:

The first datagram which is sent to a specific ip using "sendto" is lost. 收到任何短时间后发送到同一IP的数据报 . 但如果我等待一段时间(几分钟),第一次_1027546再次失去 .

广播数据报始终有效 . 本地发送(到同一台计算机)始终工作 .

我认为操作系统或路由器/交换机有一些从IP到MAC地址的转换表,在没有使用几分钟时会被遗忘,并且不幸的是导致数据报丢失 . 我可以用不同的路由器/交换机硬件观察到这种行为,所以我怀疑是windows网络层 .

我知道UDP根据定义“不可靠”,但我无法相信,即使物理连接正常工作且一切都很好,数据包也会丢失 . 然后它将毫无 Value .

从技术上讲,我打开UDP套接字,将其绑定到端口和INADRR_ANY . 然后我使用“sendto”和“recvfrom” . 我从不做连接 - 我不想,因为我有几个球员 . 据我所知,UDP应该在没有连接的情况下工作 .

我目前的解决方法是,我经常向所有特定的玩家ips发送虚拟数据报 - 这解决了问题,但它在某种程度上“不满意”

Question: 有人知道这个问题吗?它从何而来?我该如何解决?

编辑:

我把它归结为以下测试程序:

int _tmain(int argc, _TCHAR* argv[])
{
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    SOCKET Sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    SOCKADDR_IN Local = {0};
    Local.sin_family = AF_INET;
    Local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    Local.sin_port = htons(1234);
    bind(Sock, (SOCKADDR*)&Local, sizeof(Local));
    printf("Press any key to send...\n");
    int Ret, i = 0;
    char Buf[4096];

    SOCKADDR_IN Remote = {0};
    Remote.sin_family = AF_INET;
    Remote.sin_addr.S_un.S_addr = inet_addr("192.168.1.12");  // Replace this with a valid LAN IP which is not the hosts one
    Remote.sin_port = htons(1235);

    while(true) {
        _getch();
        sprintf(Buf, "ping %d", ++i);
        printf("Multiple sending \"%s\"\n", Buf);

        // Ret = connect(Sock, (SOCKADDR*)&Remote, sizeof(Remote));
        // if (Ret == SOCKET_ERROR) printf("Connect Error!\n", Buf);
        Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
        if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
        Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
        if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
        Ret = sendto(Sock, Buf, strlen(Buf), 0, (SOCKADDR*)&Remote, sizeof(Remote));
        if (Ret != strlen(Buf)) printf("Send Error!\n", Buf);
        }
    return 0;

该程序打开一个UDP套接字,并在每次击键时连续发送3个数据报到特定的IP . 运行那个wirehark观察你的UDP流量,按一个键,等一会再按一次键 . 您不需要远程IP上的接收器,没有任何区别,除非您不会得到黑色标记为“无法访问”的数据包 . 这就是你得到的:

Wireshark Snapshot

如您所见,第一次发送启动了对IP的ARP搜索 . 虽然该搜索正在等待,但连续3次发送中的前2次丢失 . 第二次击键(IP搜索完成后)正确发送了3条消息 . 您现在可以重复发送消息,它将一直有效,直到您等待(大约一分钟,直到地址转换再次丢失)然后您将再次看到丢失 .

That means: There is no send buffer when sending UDP messages and there are ARP requests pending! All messages get lost except the last one. Also "sendto" does not block until it successfully delivered, and there is no error return!

好吧,这让我感到惊讶并让我有点难过,因为这意味着我必须忍受我当前的解决方法或实现一个只能一次发送一条消息然后等待回复的ACK系统 - 这不容易更多并且意味着许多困难 .

4 回答

  • 4

    在其他人回答之后我就发布了这篇文章,但它直接相关 .

    如果目标地址(或目标网关)没有ARP条目,Winsock会丢弃UDP数据包 .

    因此,很可能一些第一个UDP数据包被丢弃,因为当时没有ARP条目 - 与大多数其他操作系统不同,winsock仅在ARP请求完成时排队1个数据包 .

    记录here

    当该IP地址被解析为MAC地址时,ARP仅为指定的目标地址排队一个出站IP数据报 . 如果基于UDP的应用程序将多个IP数据报发送到单个目标地址而它们之间没有任何暂停,则如果没有已存在的ARP缓存条目,则可能会丢弃某些数据报 . 应用程序可以通过在发送数据包流之前调用Iphlpapi.dll例程SendArp()来 Build ARP缓存条目来弥补这一点 .

    Mac OS XFreeBSD上可以观察到相同的行为:

    当接口请求不在高速缓存中的地址的映射时,ARP将需要映射的消息排队,并在关联的相关网络上广播请求地址映射的消息 . 如果提供了响应,则缓存新映射并传输任何未决消息 . 在等待对映射请求的响应时,ARP将最多排队一个数据包;只保留最近“传输”的数据包 .

  • 1

    UDP数据包应该在接收时进行缓冲,但是UDP数据包(或持有它的以太网帧)可以在给定机器上的几个点丢弃:

    • 网卡没有足够的空间来接受它,

    • OS网络堆栈没有足够的缓冲区内存来复制它,

    • 防火墙/数据包过滤丢弃规则匹配,

    • 没有应用程序正在侦听目标IP和端口,
      监听应用程序套接字的

    • 接收缓冲区已满 .

    前两点是关于过多的流量,这可能不是这种情况 . 然后我相信第4点不适用,您的软件正在等待数据 . 第5点是关于你的应用程序没有足够快地处理网络数据 - 似乎也不是这样 .

    MAC和IP地址之间的转换是通过Address Resolution Protocol完成的 . 如果您的网络配置正确,这不会导致数据包丢失 .

    我会禁用Windows防火墙和任何防病毒/深度数据包检测软件,并使用wireshark检查线路上的内容 . 这很可能会指向正确的方向 - 如果您可以在"sent-to"机器上嗅探那些第一个数据包,那么请检查本地配置(防火墙等);如果你不这样做,那么检查你的网络 - 路径中的某些东西会干扰你的流量 .

    希望这可以帮助 .

  • 13

    呃.....它是你的计算机做ARP请求 . 当您第一次开始发送时,您的com不知道接收方的mac地址,因此它无法发送任何数据包 . 它使用接收器的IP地址来执行ARP请求以获取mac地址 . 在此过程中,您尝试发送的任何udp数据包都无法发送,因为目标mac地址仍然未知 .

    一旦你的com收到mac地址,它就可以开始发送了 . 但是,mac地址将仅保留在你com的ARP缓存中2分钟(如果你和接收者之间没有检测到其他活动)或10分钟(完全没有ARP缓存,即使连接处于活动状态) . 这就是为什么你每隔几分钟就会遇到这个问题 .

  • -2

    有人知道这个问题吗?

    真正的问题是你假设UDP数据包发送是可靠的 . 事实并非如此 .

    使用当前网络配置丢失第一个数据包这一事实确实是次要问题 . 您可能能够解决这个问题,但在任何时候您仍然容易受到数据包丢失的影响 .

    如果丢包对你来说是一个问题,那么你真的应该使用TCP . 您可以在UDP上构建可靠的协议,但除非您有充分的理由这样做,否则不建议使用 .

相关问题