到特定远程 IP 的第一条 UDP 消息丢失 [英] First UDP message to a specific remote ip gets lost

查看:35
本文介绍了到特定远程 IP 的第一条 UDP 消息丢失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个基于 LAN 的解决方案,该解决方案带有一个必须控制多个玩家"的服务器"我选择的协议是 UDP,因为它很简单,我不需要连接,我的流量只包含不时的短命令,我想使用混合的广播消息进行同步,使用单个目标消息进行玩家个人命令.

I am working on a LAN based solution with a "server" that has to control a number of "players" My protocol of choice is UDP because its easy, I do not need connections, my traffic consists only of short commands from time to time and I want to use a mix of broadcast messages for syncing and single target messages for player individual commands.

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

Multicast TCP would be an alternative, but its more complicated, not exactly suited for the task and often not well supported by hardware.

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

Unfortunately I am running into a strange problem:

使用sendto"发送到特定 ip 的第一个数据报丢失.之后短时间发送到同一 IP 的任何数据报都会被接收.但是如果我等待一段时间(几分钟),第一个sendto"会再次丢失.

The first datagram which is sent to a specific ip using "sendto" is lost. Any datagram sent short time afterwards to the same ip is received. But if i wait some time (a few minutes) the first "sendto" is lost again.

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

Broadcast datagrams always work. Local sends (to the same computer) always work.

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

I presume the operating system or the router/switch has some translation table from IP to MAC addresses which gets forgotten when not being used for some minutes and that unfortunately causes datagrams to be lost. I could observe that behaviour with different router/switch hardware, so my suspect is the windows networking layer.

我知道 UDP 从定义上讲是不可靠的",但我无法相信这会发展到如此地步,即使物理连接正常并且所有内容都定义明确,数据包也会丢失.那么它就真的一文不值了.

I know that UDP is by definition "unreliable" but I cannot believe that this goes so far that even if the physical connection is working and everything is well defined packets can get lost. Then it would be literally worthless.

从技术上讲,我正在打开一个 UDP 套接字,将其绑定到端口和 INADRR_ANY.然后我使用sendto"和recvfrom".我从不进行连接——我不想这样做,因为我有几个玩家.据我所知,UDP 应该可以在没有连接的情况下工作.

Technically I am opening an UDP Socket, bind it to a port and INADRR_ANY. Then I am using "sendto" and "recvfrom". I never do a connect - I dont want to because I have several players. As far as I know UDP should work without connect.

我目前的解决方法是定期向所有特定玩家 ip 发送虚拟数据报 - 这解决了问题,但不知何故不满意"

My current workaround is that I regularly send dummy datagrams to all specific player ips - that solves the problem but its somehow "unsatisfying"

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

Question: Does anybody know that problem? Where does it come from? How can I solve it?

我将其归结为以下测试程序:

I boiled it down to the following test program:

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...
");
    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"
", Buf);

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

程序打开一个 UDP 套接字,并在每次击键时连续发送 3 个数据报到特定 IP.通过wireshark 运行该命令,观察您的UDP 流量,按一个键,稍等片刻,然后再次按一个键.您不需要远程 IP 上的接收器,没有区别,除了您不会收到黑色标记的不可访问"数据包.这就是你得到的:

The Program opens an UDP Socket, and sends 3 datagrams in a row on every keystroke to a specific IP. Run that whith wireshark observing your UDP traffic, press a key, wait a while and press a key again. You do not need a receiver on the remote IP, makes no difference, except you wont get the black marked "not reachable" packets. This is what you get:

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

As you can see the first sending initiated a ARP search for the IP. While that search was pending the first 2 of the 3 successive sends were lost. The second keystroke (after the IP search was complete) properly sent 3 messages. You may now repeat sending messages and it will work until you wait (about a minute until the adress translation gets lost again) then you will see dropouts again.

这意味着:发送UDP消息时没有发送缓冲区,并且有ARP请求待处理!除最后一条消息外,所有消息都会丢失.还有sendto"在投递成功之前不会阻塞,也没有错误返回!

嗯,这让我很吃惊,也让我有点难过,因为这意味着我必须接受我目前的解决方法,或者实现一个一次只发送一条消息然后等待回复的 ACK 系统 - 这不会再简单,也意味着许多困难.

Well, that surprises me and makes me a little bit sad, because it means that I have to live with my current workaround or implement an ACK system that only sends one message at a time and then waits for reply - which would not be easy any more and imply many difficulties.

推荐答案

这个问题在别人回答了很久之后才发,但它是直接相关的.

I'm posting this long after it's been answered by others, but it's directly related.

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

Winsock drops UDP packets if there's no ARP entry for the destination address (or the gateway for the destination).

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

Thus it's quite likely some of the first UDP packet gets dropped as at that time there's no ARP entry - and unlike most other operating systems, winsock only queues 1 packet while the the ARP request completes.

这是记录在这里:

ARP 仅对指定目的地的一个出站 IP 数据报进行排队将 IP 地址解析为 MAC 地址时的地址.如果一个基于 UDP 的应用程序将多个 IP 数据报发送到单个目标地址之间没有任何停顿,其中一些如果还没有 ARP 缓存条目,则可能会丢弃数据报展示.应用程序可以通过调用Iphlpapi.dll 例程 SendArp() 建立一个 ARP 缓存条目,之前发送数据包流.

ARP queues only one outbound IP datagram for a specified destination address while that IP address is being resolved to a MAC address. If a UDP-based application sends multiple IP datagrams to a single destination address without any pauses between them, some of the datagrams may be dropped if there is no ARP cache entry already present. An application can compensate for this by calling the Iphlpapi.dll routine SendArp() to establish an ARP cache entry, before sending the stream of packets.

Mac OS XFreeBSD:

当一个接口请求一个地址的映射时,没有在缓存中,ARP 将需要在关联的关联上映射和广播消息请求地址映射的网络.如果响应是提供,新映射被缓存,任何未决消息传送.ARP 在等待时最多排队一个数据包对映射请求的响应;只有最近的保留已传输"数据包.

When an interface requests a mapping for an address not in the cache, ARP queues the message which requires the mapping and broadcasts a message on the associated associated network requesting the address mapping. If a response is provided, the new mapping is cached and any pending message is transmitted. ARP will queue at most one packet while waiting for a response to a mapping request; only the most recently ``transmitted'' packet is kept.

这篇关于到特定远程 IP 的第一条 UDP 消息丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆