客户端用asynselect,一收到服务器的消息就立即发送数据给服务器。中间没有暂停,此时服务器的内存会慢慢减少。如果客户端暂停发送与接收,服务器的内存又会慢慢增加,再接着发送数据服务器的内存又慢慢减少。
请问为什么服务器会产生这样的问题?该如何解决。
请问为什么服务器会产生这样的问题?该如何解决。
解决方案 »
- Delphi入门问题:创建带窗口的DLL
- indy传输内存流的问题
- 在使用INDY控件的时候,出现Uneven size in DecorderToStream是什么原因?
- 如何编写一个函数,对于给定的一个字符窜,去掉其中不是英文字母的字符。急救。答对30分
- 截取函数使用啊!
- 请问DBGRIDEH 中如何点击列名实现自动排序?分不多,还请大家帮帮忙
- Delphi从入门到精通,该读哪些书?
- dbgrid组件的斑马线显示
- 重新提问:怎样把DBNavigator上的图标改为汉字?比如改为第一条,前一条,后一条,插入,删除等等。
- Delphi如何实现内存驻留?(如:杀毒软件的驻留法)
- 如何申请指定大小内存区?
- 各位大哥:水晶报表ireport对象使用printout报错,有谁能帮帮我
(引自flashboy (爱写程序的小绵羊) )
一、 WSAENOBUFS 错误问题。 这个问题通常很难靠直觉发现,因为当你第一次看见的时候你或许认为是一个内存泄露错误。假定已经开发完成了你的完成端口服务器并且运行的一切良好,但是当你对其进行压力测试的时候突然发现服务器被中止而不处理任何请求了,如果你运气好的话你会很快发现是因为WSAENOBUFS 错误而影响了这一切。 每当我们重叠提交一个send或receive操作的时候,其中指定的发送或接收缓冲区就被锁定了。当内存缓冲区被锁定后,将不能从物理内存进行分页。操作系统有一个锁定最大数的限制,一旦超过这个锁定的限制,那么就会产生WSAENOBUFS 错误了。 如果一个服务器提交了非常多的重叠的receive在每一个连接上,那么限制会随着连接数的增长而变化。如果一个服务器能够预先估计可能会产生的最大并发连接数,服务器可以投递一个使用零缓冲区的receive在每一个连接上。因为当你提交操作没有缓冲区时,那么也不会存在内存被锁定了。使用这种办法后,当你的receive操作事件完成返回时,该socket底层缓冲区的数据会原封不动的还在其中而没有被读取到receive操作的缓冲区来。此时,服务器可以简单的调用非阻塞式的recv将存在socket缓冲区中的数据全部读出来,一直到recv返回 WSAEWOULDBLOCK 为止。 这种设计非常适合那些可以牺牲数据吞吐量而换取巨大并发连接数的服务器。当然,你也需要意识到如何让客户端的行为尽量避免对服务器造成影响。在上一个例子中,当一个零缓冲区的receive操作被返回后使用一个非阻塞的recv去读取socket缓冲区中的数据,如果服务器此时可预计到将会有爆发的数据流,那么可以考虑此时投递一个或者多个receive来取代非阻塞的recv来进行数据接收。(这比你使用1个缺省的8K缓冲区来接收要好的多。)总结: 解决方法一: 投递使用空缓冲区的 recevie操作,当操作返回后,使用非阻塞的recv来进行真实数据的读取。因此在完成端口的每一个连接中需要使用一个循环的操作来不断的来提交空缓冲区的receive操作。解决方法二: 在投递几个普通含有缓冲区的recevie操作后,进接着开始循环投递一个空缓冲区的recevie操作。这样保证它们按照投递顺序依次返回,这样我们就总能对被锁定的内存进行解锁。
昨晚又做了一个简单的wasAsynSelect的客户端进行测试。服务器端是最简单的iocp server。发现连续不断的测试,并没有导致服务器端的内存减少。看来要好好调试一下原来的服务器端程序了。
客户端用send recv连续发送和接收,过一段时间就会堆栈溢出。后来换成wsasend wsarecv就再也不会出现堆栈溢出。