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