情况相当简单。。。不允许使用“sendto()
”,因此改用“send()
”。。。
在winsock2.2
下,在运行Windows 7 Professional
的全新i7机器上正常运行。。。
使用SOCK_DGRAM
套接字,客户端和服务器控制台应用程序通过localhost
(127.0.0.1
)连接来测试。。。
必须使用固定大小的包。。。
客户端套接字使用connect()
,服务器套接字使用bind()
。。。
客户端使用一系列BLOCKING
send()
调用发送N个数据包。服务器只使用带有ioctlsocket
的FIONREAD
调用,在while
循环中运行以不断地printf()
等待接收的字节数。。。
包会丢失,除非我花了大量时间。。。我的意思是,如果不使用SLEEP()
,接收套接字上的字节数在不同的运行中是不同的。。。
曾经玩过改变缓冲区大小的游戏,情况没有太大变化,只是现在没有溢出,但是延迟的问题还是一样的。。。
我已经看到很多关于SLEEP()
和send()
之间的问题的讨论,但是在这个场景中,recv()
甚至没有涉及到。。。
有人在想吗?
(另请注意,我编程时所受的限制是出于超出我控制范围的原因,因此不需要recv()
、WSA
、.NET
、MFC
、STL
、BOOST
、QT
、recv()
或其他东西)
这不是缓冲区溢出问题,原因有三:
传入缓冲区和传出缓冲区都被设置并检查为
比发送的所有信息都要大得多。
没有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数据包丢失仍然会发生。所以不管怎样,你需要设计一个允许你的程序仍然以一种合理有用的方式运行,即使包丢失了。这可以通过实现某种自动重发机制来实现,或者(取决于您要完成的任务)通过设计协议来实现,这样就可以简单地忽略数据包丢失。