Socket缓冲区满,请大侠出招

Leo_red 2008-07-09 09:44:34
小弟最近写个测试程序,一个程序起1000个线程模拟1000个客户端,每线程创建一个socket长连接向服务器发起连接并传送数据,每线程采用重叠IO的方式传送18K的数据后等待几分钟,再次发送,如此循环。
测试中服务器每次都正确收到了数据(显示接受数据的大小和发送的一样),但是客户端却发送到第三个循环以后就出现10055的错误(缓冲区满或队列满)。照理说服务器收取了数据以后客户端的发送缓冲区应该被释放掉,为什么会有这样的问题?

看了前辈们的帖子,也尝试在客户端的WSASend之前先select检查一下,如果缓冲满了就让线程等待5秒再试,但是实际测试发现程序怎么骗过了select以后还是在WSASend的时候出现缓冲区满的错误,小弟无计可施,请大侠们指点。
...全文
2175 21 打赏 收藏 转发到动态 举报
写回复
用AI写文章
21 条回复
切换为时间正序
请发表友善的回复…
发表回复
嘿-嘿-嘿 2008-07-16
  • 打赏
  • 举报
回复
难道我的那压力测试程序 也需要用overlapped来写?

问下,是不是每个客户端的新建的线程 都需要调用WSAstarup()一次?

然后每个线程 都需要socket,connect。。。。???

是这样么?
Leo_red 2008-07-10
  • 打赏
  • 举报
回复
终于找到这个帖子应该放的地方了,再顶起来,请大侠们帮帮忙,谢谢了
Leo_red 2008-07-10
  • 打赏
  • 举报
回复
说的是,确实我的代码也有点问题,没有很好的利用检测或是检测的方式不对,所以过频繁的投递给了系统很大的负担。
多谢各位!
WinEggDrop 2008-07-10
  • 打赏
  • 举报
回复
[Quote=引用 18 楼 Leo_red 的回复:]
14、15、16楼的兄弟的贴给了我提示,我把之前的18K数据投递发送1000遍改为1000个18K的数据打包一次发送,这样还是1000个线程,结果问题就解决了,确实没有再出现10055的错误。是过于频繁的投递给了操作系统太大压力了。

在之前出问题的时候,我发现测试程序占用的页面缓冲池爆高,十几甚至几十兆了,句柄数也爆高。现在这样改了以后页面缓冲池也就300K的样子,属于正常了。

另外在请教WinEggDrop,我现在测试的运行环境是…
[/Quote]

局部网是10Mbps/100Mbps的网卡,但经常是无法到达100Mbps的.还会出现阻塞是肯定的.如果你使用的是阻塞的发送,那么只能是出现缓冲满发不出时,Sleep()一会或使用slect()测试socket是否继续可写,再尝试发送.如果你使用非阻塞异步的发送,那么缓冲满后,一般缓冲可以继续发送后,会有消息或信号通知的,当收到缓冲可写的消息或信号后,继续发送.
Leo_red 2008-07-10
  • 打赏
  • 举报
回复
14、15、16楼的兄弟的贴给了我提示,我把之前的18K数据投递发送1000遍改为1000个18K的数据打包一次发送,这样还是1000个线程,结果问题就解决了,确实没有再出现10055的错误。是过于频繁的投递给了操作系统太大压力了。

在之前出问题的时候,我发现测试程序占用的页面缓冲池爆高,十几甚至几十兆了,句柄数也爆高。现在这样改了以后页面缓冲池也就300K的样子,属于正常了。

另外在请教WinEggDrop,我现在测试的运行环境是局域网,网络环境比较好。但是确实我超过了100Mbps的带宽,不过我给了发送的缓冲时间,不是阻塞的发送,带宽不够的压力似乎不会太大吧?

不过问题已经解决了,明天结贴给分,现在纯粹讨论一下。
sanshao27 2008-07-10
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 WinEggDrop 的回复:]
引用楼主 Leo_red 的帖子:
小弟最近写个测试程序,一个程序起1000个线程模拟1000个客户端,每线程创建一个socket长连接向服务器发起连接并传送数据,每线程采用重叠IO的方式传送18K的数据后等待几分钟,再次发送,如此循环。
测试中服务器每次都正确收到了数据(显示接受数据的大小和发送的一样),但是客户端却发送到第三个循环以后就出现10055的错误(缓冲区满或队列满)。照理说服务器收取了数据以后客户端的发送缓冲区应该…
[/Quote]
WinEggDrop 2008-07-10
  • 打赏
  • 举报
回复
[Quote=引用楼主 Leo_red 的帖子:]
小弟最近写个测试程序,一个程序起1000个线程模拟1000个客户端,每线程创建一个socket长连接向服务器发起连接并传送数据,每线程采用重叠IO的方式传送18K的数据后等待几分钟,再次发送,如此循环。
测试中服务器每次都正确收到了数据(显示接受数据的大小和发送的一样),但是客户端却发送到第三个循环以后就出现10055的错误(缓冲区满或队列满)。照理说服务器收取了数据以后客户端的发送缓冲区应该被释放掉,为什么会有这样的…
[/Quote]

你要看你自己的带宽呀.1000个线程,每个线程发18K数据,同时1000个线程发送的话,那么使用的带宽最高能达到18000 KB,那是18MB的数据,超过100Mbps了.你一般的系统根本不可能有这样大的带宽.就算是租的服务器,一般也只是保证5MB左右带宽,更不要说你家用的ADSL等了.缓冲区满是肯定的,不出现才叫奇怪.
cppwin 2008-07-10
  • 打赏
  • 举报
回复
程序中做统计,发出了多少个wsasend, 已经完成了多少个,
它的差是多少, 当出现WSAENOBUF时, 这个值就是你的机器内存允许的极限.
annvily 2008-07-10
  • 打赏
  • 举报
回复
可能发送频率太快了
Leo_red 2008-07-09
  • 打赏
  • 举报
回复
首先多谢兄弟的关注。

1000个线程确实可以在Windows下面起来的,可以在任务管理器里面,选择列时选择线程数,然后查看我队应的进程有1001个线程,恰好是1000个工作线程+1个主线程。

服务器端在每次accept成功以后会对一个全局的变量增加1(这个变量的增加和减少都是线程保护了的),并且会显示当前连接的客户端数目。在测试中我也手工去telnet那个机器的队应端口,可以看到服务器显示连接数目就会+1,我觉得这个还是比较准确的。

而且我也命令行下面用netstat查看网络连接的情况,服务器下确实服务器和客户端都能看到1000个连接是ESTABLISHED的,连接上面应该也没有问题。
fuqd273 2008-07-09
  • 打赏
  • 举报
回复
数据部分打上线程专有的数据标签啊,这个标签可以你自己设计。

我个人从测试程序的角度考虑,对于客户端能否保证1000个线程的长连接存疑。理由是创建新线程的线程个数是有操作系统限制的。首先需要验证的就是能否正常创建1000个线程。在unix环境的话,是需要修改操作系统内核参数的,普通一个进程只能创建63个线程,计入初始线程总计64个。

其次,不清楚你的服务器端是怎么能够判断出来“1000个线程连接成功后就没有再收到新的连接”的,是否是有这样的测试要求。要求同时建立1000个连接?
如果只是要求同时1000个连接的话,多进程更好控制。
如果你的服务器端的计算连接个数的方法存在问题,例如实际上不存在1000个连接的话……你的测试本身就存在很大问题了,现象反而是次要的。

Leo_red 2008-07-09
  • 打赏
  • 举报
回复
是Windows下的程序,第一次发帖,不知帖到哪里合适,还请多包涵。

初始化socket时候设置了SO_KEEPALIVE,底层实现保活。
每个线程只有在socket出错的时候才会closesocket,然后重新创建socket,再去connect。

从服务器端看来,1000个线程连接成功后就没有再收到新的连接,头三次发送数据都是在旧的socket上发送的,没有新连接,并且貌似都正常。
只有WSASend出了10055的错误以后,客户端才closesocket,再去尝试创建新的socket和连接。

目前为了测试(想尽快填写报告),所以客户端每次发送的间隔是4分钟,如果尝试半小时的间隔会不会好一点?

主要不解的是从服务器端看收到的数据和发送的数据总量是一致的,应该客户端的发送缓冲是清空掉的阿。
或者有什么更可靠的方法来确认收到了数据吗?
fuqd273 2008-07-09
  • 打赏
  • 举报
回复
WSASend....
windows下的程序?

unix下有线程个数限制,特别你又是长连接而不是短连接。请问你的1000个长连接能保证么?
单纯靠字节数检查数据正确性显然可靠性不足。

至于windows环境的多线程socket编程,我了解不多。但是线程个数有一定限制这一点是可以肯定的。
那么还是这个问题,请问,你的1000个长连接能保证么?
快乐田伯光 2008-07-09
  • 打赏
  • 举报
回复
最近再写一个balancer,
楼主的程序来做我的balancer测试程序极好啊.呵呵
Leo_red 2008-07-09
  • 打赏
  • 举报
回复
边研究边顶起来
fuqd273 2008-07-09
  • 打赏
  • 举报
回复
考虑到硬件层的实际情况,硬件回路只有一条。
底层transport's buffer的唯一性应该是可以预期的。

不过,如果楼主能肯定是在第四个循环必然出现问题的话,建议楼主查一下,到底是第几条数据出问题。可能是测试程序的问题,例如内存访问越界。
Leo_red 2008-07-09
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 fuqd273 的回复:]
倒是多线程情况下,由于系统transport's buffer的唯一性,很难保证不会发生dead lock。
所以,deadlock的可能性大于缓存溢出。[/Quote]

也就是说,虽然每一个Socket各自有发送缓冲和接收缓冲,但是系统底层在操作发送和接收的时候还是有一个统一的缓冲队列?
如果真是这样,我1000个线程毫无次序的去投递发送,确实很难说脆弱的Windows会不会出现deadlock。

因为我也尝试setsockop设置大发送缓冲区,但是除了降低了发送效率以外没有其他收获。

刚才又尝试了发送间隔为半个小时,还是在发送第四个循环的时候出现10055错误了。看来是和发送到了一定数量以后就出现了错误的,我再尝试一下一次发送数据量加大的情况。

其实起了这么多线程,无非是想模拟一下并发的1000连接下,服务端的接受和响应能力,加了一定的互斥,会不会因为让线程有序而不能很好模拟无序的大量连接。
再者,我还没想到往哪里加互斥,呵呵。
fuqd273 2008-07-09
  • 打赏
  • 举报
回复
[code=BatchFile]For non-overlapped sockets, the last two parameters (lpOverlapped, lpCompletionRoutine) are ignored and WSASend adopts the same blocking semantics as send. Data is copied from the buffer(s) into the transport's buffer. If the socket is non-blocking and stream-oriented, and there is not sufficient space in the transport's buffer, WSASend will return with only part of the application's buffers having been consumed. Given the same buffer situation and a blocking socket, WSASend will block until all of the application buffer contents have been consumed.

Note The socket options SO_RCVTIMEO and SO_SNDTIMEO apply only to blocking sockets.

If this function is completed in an overlapped manner, it is the Winsock service provider's responsibility to capture the WSABUF structures before returning from this call. This enables applications to build stack-based WSABUF arrays pointed to by the lpBuffers parameter.

For message-oriented sockets, do not exceed the maximum message size of the underlying provider, which can be obtained by getting the value of socket option SO_MAX_MSG_SIZE. If the data is too long to pass atomically through the underlying protocol the error WSAEMSGSIZE is returned, and no data is transmitted.

[/code]

综合这些说明看起来,这个API本身应该可以保证不会缓存溢出。
倒是多线程情况下,由于系统transport's buffer的唯一性,很难保证不会发生dead lock。
所以,deadlock的可能性大于缓存溢出。
fuqd273 2008-07-09
  • 打赏
  • 举报
回复
msdn还是尽量使用英文版,中文翻译的msdn包括官方的msdn都不敢恭维。

怀疑还是加上线程间互斥逻辑比较好。
没想好理由……

底层的逻辑我也没研究过,毕竟这是api的内部逻辑,,,,

不过,看到这个函数的说明里面写着:
[code=BatchFile]The WSASend function provides functionality over and above the standard send function in two important areas:

It can be used in conjunction with overlapped sockets to perform overlapped send operations.
It allows multiple send buffers to be specified making it applicable to the scatter/gather type of I/O.
[/code]

而你又说你使用了重叠IO,怀疑重叠IO的方式下,使用多线程的IO操作还是需要线程互斥的。
具体要看楼主的调用方式了。
Leo_red 2008-07-09
  • 打赏
  • 举报
回复
对阿,确实没有加互斥逻辑,因为Client每个线程都是独立的,不觉得有需要互斥的地方啊。
另外顺路请教一下,是系统为每个socket都分配各自的发送和接收缓冲区的吧?那么这些线程各自工作,似乎也不相干。

WSAENOBUFS 10055确实用errorlook查看时候显示:
由于系统缓冲区空间不足或列队已满,不能执行套接字上的操作。

而WSAEWOULDBLOCK 10035查看的解释是:
无法立即完成一个非阻挡性套接字操作。

似乎我还没有遇上返回10035的错误.

不过你的提醒让我觉得我发送的数据量不算大,重叠的操作是应该已经完成了的,也就是说每次发送都是正常发出去了的。真正的错误在于系统底层死锁了缓冲区,造成之后的所有操作都失败了。因为我也发现出现问题以后,netstat查看连接数量骤减,貌似只有2~3个连接可以连上,其余都连不上了。

什么情况会造成这种缓冲区的死锁呢?我的程序是不是有什么办法避免?


加载更多回复(1)

18,356

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 网络编程
c++c语言开发语言 技术论坛(原bbs)
社区管理员
  • 网络编程
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧