我正在开发一个基于 LAN 的解决方案,其中一个“服务器”必须控制多个“玩家”
我选择的协议是 UDP,因为它很简单,我不需要连接,我的流量仅由时不时的短命令组成,我想使用混合广播消息进行同步,并使用单个目标消息进行玩家单独的命令。
多播 TCP 是一种替代方案,但它更复杂,不完全适合该任务,并且通常得不到硬件的良好支持。
不幸的是我遇到了一个奇怪的问题:
使用“sendto”发送到特定 IP 的第一个数据报丢失。稍后发送到同一 IP 的任何数据报都会被接收。
但如果我等待一段时间(几分钟),第一个“sendto”就会再次丢失。
广播数据报始终有效。
本地发送(到同一台计算机)始终有效。
我推测操作系统或路由器/交换机有一些从 IP 到 MAC 地址的转换表,在几分钟不使用时会被遗忘,不幸的是会导致数据报丢失。
我可以用不同的路由器/交换机硬件观察到这种行为,所以我怀疑是 Windows 网络层。
我知道 UDP 根据定义是“不可靠的”,但我无法相信这种情况会发展到即使物理连接正常工作并且一切都定义良好的数据包也可能丢失。那么它就真的毫无价值了。
从技术上讲,我正在打开一个 UDP 套接字,
将其绑定到端口和 INADRR_ANY。
然后我使用“sendto”和“recvfrom”。
我从不进行连接——我不想这样做,因为我有几个玩家。据我所知,UDP 应该在没有连接的情况下工作。
我当前的解决方法是定期向所有特定玩家 ip 发送虚拟数据报 - 这解决了问题,但在某种程度上“不令人满意”
问题:有人知道这个问题吗?它从何而来?我该如何解决?
Edit:
我将其归结为以下测试程序:
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。
运行whithwireshark观察你的UDP流量,按下一个键,等待一段时间,然后再次按下一个键。
您不需要远程 IP 上的接收器,这没有什么区别,除非您不会收到黑色标记的“无法到达”数据包。
这就是你得到的:
正如您所看到的,第一次发送启动了 IP 的 ARP 搜索。当搜索等待时,连续 3 次发送中的前 2 次丢失。
第二次击键(IP 搜索完成后)正确发送了 3 条消息。
您现在可以重复发送消息,它会一直工作,直到您等待(大约一分钟,直到地址翻译再次丢失),然后您将再次看到丢失。
这意味着:发送 UDP 消息时没有发送缓冲区,并且有 ARP 请求待处理!除最后一条消息外,所有消息都会丢失。
另外“sendto”在发送成功之前不会阻塞,并且不会有错误返回!
好吧,这让我感到惊讶,也让我有点难过,因为这意味着我必须接受当前的解决方法,或者实现一个 ACK 系统,一次只发送一条消息,然后等待回复 - 这对任何人来说都不容易更多,意味着许多困难。