情况相当简单。。。不允许使用“sendto()”,因此改用“send()”。。。
winsock2.2下,在运行Windows 7 Professional的全新i7机器上正常运行。。。
使用SOCK_DGRAM套接字,客户端和服务器控制台应用程序通过localhost127.0.0.1)连接来测试。。。
必须使用固定大小的包。。。
客户端套接字使用connect(),服务器套接字使用bind()。。。
客户端使用一系列BLOCKINGsend()调用发送N个数据包。服务器只使用带有ioctlsocketFIONREAD调用,在while循环中运行以不断地printf()等待接收的字节数。。。
包会丢失,除非我花了大量时间。。。我的意思是,如果不使用SLEEP(),接收套接字上的字节数在不同的运行中是不同的。。。
曾经玩过改变缓冲区大小的游戏,情况没有太大变化,只是现在没有溢出,但是延迟的问题还是一样的。。。
我已经看到很多关于SLEEP()send()之间的问题的讨论,但是在这个场景中,recv()甚至没有涉及到。。。
有人在想吗?
(另请注意,我编程时所受的限制是出于超出我控制范围的原因,因此不需要recv()WSA.NETMFCSTLBOOSTQTrecv()或其他东西)
这不是缓冲区溢出问题,原因有三:
传入缓冲区和传出缓冲区都被设置并检查为
比发送的所有信息都要大得多。
没有ioctl(),只有通过Sleep()调用检查传入缓冲区,在用户输入后很长时间内调用recv()。
当在>40ms-s之间添加send()Sleep()时,整件事都会工作,即如果出现溢出,则
again, see point (2)会有帮助的()

最佳答案

数据包会丢失,除非我在SLEEP()中放入大量
时间。。。我的意思是接收套接字上的字节数
如果不使用SLEEP(),则不同的运行。。。
这是预期的行为;正如其他人在评论中所说,UDP数据包可以也确实会因为任何原因被丢弃。然而,在仅本地主机通信的上下文中,原因通常是某个固定大小的包缓冲区已满,无法容纳传入的UDP包。注意,UDP没有流控制的概念,所以如果你的接收程序跟不上你的发送程序,一旦缓冲区满了,包丢失肯定会发生。
至于该怎么做,insert-a-call-to-sleep()解决方案不是特别好,因为您无法很好地知道“正确”的睡眠时间应该是什么。(缩短sleep(),您仍然会丢弃数据包;过长的sleep(),您传输数据的速度比其他方式慢;当然,“最佳”值可能会因计算机的不同而有所不同,或因时间的不同而以不明显的方式有所不同)。
您可以做的一件事是切换到不同的传输协议,如TCP,或者(因为您只在本地主机中通信),一个简单的管道或socketpair。这些协议具有您正在寻找的无损FIFO语义,因此它们可能是正确的工具。
但是,假设您需要使用UDP,UDP数据包丢失对您来说将是一个不争的事实,但是您可以做一些事情来减少数据包丢失:
send()处于阻塞模式,或者如果使用非阻塞send(),请确保在调用send()之前等待UDP socket select()准备好写入。(我知道你说你在阻塞模式下发送(),我只是为了完整起见而包含这个)
使接收UDP套接字上的SO RCVBUF设置尽可能大。缓冲区越大,填充到容量的可能性就越低。
在接收程序中,确保调用recv()的线程不会执行任何其他阻止它返回下一个recv()调用的操作。特别是,无阻塞操作(甚至printf()也是一种阻塞操作,它可以减慢线程的运行速度,特别是在Windows下,DOS提示符因加载时的慢速滚动而臭名昭著)
在单独的线程中运行接收方的network recv()循环,该线程只调用recv()并将接收到的数据放入FIFO队列(或其他共享数据结构)中的某个位置。然后,另一个线程可以做更少的时间关键的工作来检查和解析FIFO中的数据,而不必担心会导致数据包丢失。
以最高优先级运行UDP接收线程,您可以说服操作系统让您运行。可以保留UDP接收线程的其他任务越少,在这些保留期间丢弃数据包的机会就越少。
只要记住,无论你多么聪明地减少UDP数据包丢失的机会,UDP数据包丢失仍然会发生。所以不管怎样,你需要设计一个允许你的程序仍然以一种合理有用的方式运行,即使包丢失了。这可以通过实现某种自动重发机制来实现,或者(取决于您要完成的任务)通过设计协议来实现,这样就可以简单地忽略数据包丢失。

08-17 17:02
查看更多