先说说阻塞式同步IO
Socket要实现通信,最简单的函数是Write和Read。而这两个函数在默认时都是阻塞式的。阻塞式的意思就是调用后会挂起线程,直到达成某一个目标才继续执行。
这么做的坏处显而易见,服务器没法快速的和对个客户端同时进行通信了(或者说效率暴低)。
首先当然可以用异步io的方式,但这不是我们这段讨论的内容,同步解决的办法有这些。
Receive
使用多路复用的方法(Select,Epoll)
这个做法的原理就是每次收数据前,会看一下Socket有没有数据,没有数据,就不去Receive。这么做也就避免了阻塞,解放了线程。
Send
设置Socket的属性IsBlocking=false;
这时的socket成为了一个非阻塞的socket,每次调用Send时,如果没有数据可以发送或没有全部发送出去,会返回实际发送的字节数。
Send到底阻塞在哪里?
相对于Receive(别人不给我数据,我只能等)。Send为什么会在IsBlocking=true的时候阻塞比较长时间呢?这难道不是和写文件一样,只是一次确定的系统调用消耗吗?虽然实际应用中,不太会这么设置,这里我们还是想刨根问底研究下。
Send阻塞时间到底有哪几部分组成?
把数据从用户程序拷贝到发送缓冲区时间T1,如果发送缓冲区满了,就会阻塞直到缓冲区重新可用T2
T1是正比于数据量的,所以一般是非常短的时间。
T2就比较复杂了,而且可能会比较长。它受影响与缓冲区的大小,以及通信对端的速度。
缓冲区越大,T2的情况就不容易发生,T2理论上就比较短。而对端的速度造成影响的原因,主要是因为tcp滑动窗口的存在。通过write发送的数据,被放在发送缓冲并发给客户端,此时缓冲的数据并不会删除,一直到客户端ack了一个数据段。那么,如果客户端非常慢,就可能出现一直不ack服务器的数据,导致服务器缓冲区一直满负载,一直阻塞住,T2就爆了。
这种情况,我目前也没有很好的办法,只能设置一个超时时间(SendTimeout=500ms)补救下。
这里说道的内容,想具体了解的朋友可以参阅《TCP/IP卷一》