IOCP 端口注意事项

来源:互联网 发布:淘宝触屏版手机版首页 编辑:程序博客网 时间:2024/05/23 11:27

 IOCP 端口思想:

 我们不停地发出异步的WSASend/WSARecv I/O操作,具体的I/O处理过程由WINDOWS系统完成,WINDOWS系统完成实际的IO处理后,

 把结果送到完成端口上(如果有多个I/O都完成了,那么就在完成端口那里排成一个队列)。

 我们在另外一个线程里从完成端口不断地取出I/O操作结果,然后根据需要再发出WSASend/WSARecv IO操作。

 

  工作线程是处理 在 完成端口的消息完成的。。。WSASend(). 和 WSARecv()是交给系统去完成的。。。如果完成了。。然后发送结果到完成端口对列中,,,工作线程循环查询 队列中是消息。。然后根据I/O类型再处理。

  工作线程都干些什么呢?首先是调用::GetQueuedCompletionStatus()函数在关联到这个完成端口上的所有套接字上等待I/O的完成。

  再判断完成了什么类型的I/O,处理。

 

 IOCP..核心要点。。为每一个连接的SOCKET 投递一个接受请求。。。接受请求完成。。。然后自己处理。。然后再投递一个接受请求。。。记住要对应你的SOCKET.

 IOCP提供了解决“每个客户端占用一个线程”的瓶颈问题的办法,只使用几个处理线程,异步输入/输出来发送/接收。

 

 注意: 

WSASend() 和 WSARecv() 一定用不同的IO_OPERATE  这样才能判断到底是那个I/O类型完成了,千万不弄混。 

  一个好的IOCP设计。。。如果对方传文件的话,,要接受文件的长度。。如果文件的长度大于接受缓冲区的话。那么就用多个WSARECV接受同一个文件。。然后接受后再重组。。

 多线程 异步发送数据。。你必须控制你的发送缓冲区和接受缓冲区。。。

 

 下面是问题.

  如果循环发送WSASend() 或者 WSARecv() WSAAccept()和 其他的 多次发送WSASend()或者 WSARecv() WSAAccept(会引起。。发送缓冲区的不足 或者 接受缓冲区的不足。。。。

一定要根据strlen(str).设置发送的长度...IOCP 多次投递WSAsend() 问题因为不仅有接受缓冲。。也有发送缓冲。。发送的数据超过发送缓冲的时候。

 造成发送的缓冲不足,,会造成内存锁定

  发送缓冲区和接受缓冲区是非常宝贵的。。所以你每次发送的时候都要根据发送的数据内存的长度,限制她的发送长度。

  接受数据的时候。。也可以先接受一个要发送方发来的一个要将接受数据的长度的数据包。。然后根据这个长度,重新开辟自己的接受缓冲区。

  当然,你也可以用多个接受线程来接受一个数据包。。因为你的接受缓冲区是共享的。所以效率更高点。。然后你再重组包。

  还有如果发送的数据长度,超过发送缓冲区的话。。就会分几次发送。。。如果接受的数据长度,超过接受缓冲区的话,也就会分几次接受。

 

WSAENOBUFS问题

 假定已经开发完成了你的完成端口服务器并且运行的一切良好,但是当你对其进行压力测试的时候突然发现服务器被中止而不处理任何请求了,

 如果你运气好的话你会很快发现是因为WSAENOBUFS 错误而影响了这一切。 

 每当我们重叠提交一个send或receive操作的时候,其中指定的发送或接收缓冲区就被锁定了。当内存缓冲区被锁定后,将不能从物理内存进行分页。

 操作系统有一个锁定最大数的限制,一旦超过这个锁定的限制,那么就会产生WSAENOBUFS 错误了。

 要记住,如果重叠操作调用失败时(也就是说,返回值是SOCKET_ERROR,并且错误原因不是WSA_IO_PENDING),那么完成端口将不会收到任何完成通知。

 如果重叠操作调用成功,或者发生原因是WSA_IO_PENDING的错误时,完成端口将总是能够收到完成通知。

 

 每发出一个AcceptEx() WSARecv()时我们都同时需要为它提供一个接收缓冲区,,WSASend()有一个发送缓冲区,那么内存中将会出现很多被锁定的页面

 (前文说过了,每个重叠操作都会消耗一小部分未分页内存池,同时还会锁定所有涉及的缓冲区。)

 

 每一次系统调用都是有开销的.系统繁忙的时候,总是有很多请求等待处理,轮到你的会话发送数据了,就应该一次把要发的都发完,机会错过了,就得排队,

 等其他请求完成了才再一次轮到你的第二次写请求.       

  应用层的发送请求应该有所对应的会话缓冲起来,轮到它了就一次发完.每一次wsasend的时候,

  OS要么 把你那块内存拷贝到内核缓冲区,要么把你那块内存锁住,等到适当的时候发送这块内存的数据,内核地址空间是共享的,是个稀缺货,应该尽量避免大量使用.

 

 发送缓冲不足的时候,会内存锁定  如果一次向WSASend投递多个wsabuf会导致系统锁定多个页面或者占用内核缓冲区么??。。思考。

 每次系统调用的开 销是相对比较大的.是的,所以我一次投递多个wsabuf的主要原因就是:1 一次性投递多个数据,减少系统调用的次数 

 

 

   避免投递顺序的问题    包的处理。

 

  在tcp下,对于一个socket同时投递多个send,不说send端会怎么样,对于recv端将是个灾难!tcp是流式协议,会有半包、粘包,

  这样recv 端将有可能收到几次send出来的部分数据拼凑起来的不可解析数据... recv同时投递多个,在线程切换时,会造成数据乱序,在半包粘包时数据将不可解析。

  个人经验,tcp时,对于一个socket目标,同时只能有1次send,1次recv 

 

   IOCP 只提供一个尽量高效的I/O机制,只管数据的收发,包的处理还是自己的,包要自己在应用层设计和解析.

 

   解决方法看来只有3种,第一种就是确定第一个包投递完整之后,再投递第二个。那就不得不阻塞 

   第二种就是在包上面加标记,接收的时候组包(好麻烦的。。。。) 

   第三种,也是我目前用的方法,就是自己建立有个缓冲区,把要投递的数据放入缓冲区,然后交给WSASend去投递,

  后来的数据依次放入缓冲区中,这样就可以避免投递顺序的问题。。。 

  

  其他:

  (待验证。)

   发现每次只投递小于路由器MTU的包,都可以完整的收发包。100%的可以。

   MTU通常是1096,所以发 <1024字节的包比较合适,要是大于1024,在WSASend时就作多包发送,然后就需要重组,和排序。

   

   (待验证。)

   从整个系统来看,比如你有1万个连接,连接数多了后,你考虑的问题不是单个SOCKET要有多块,

    而是系统资源和带宽如何平均地分配给这么多个连接。