socket缓冲区
每个socket被创建后,会分配两个缓冲区——输入缓冲区和输出缓冲区
发送数据:write()/send()函数并不会立即向网络中传输数据,而是先将数据存入输出缓冲区中,然后由TCP协议将数据发送到目标机器。一旦数据进入到缓冲区中,函数就可以成功返回了,不管数据有没有被发送到目标机器,也不管它何时被发送,这是TCP需要负责的事情。
TCP协议独立于write()/send()函数,数据有可能刚被写入到缓冲区就被发送到网络,也有可能多次写入后不断积压,然后一次性全部发送到网络,这取决于当前的网络状况,以及当前线程是否空闲等诸多因素,并不由程序员控制
接收数据:read()/recv()函数也是一样,并不从网络中直接读取数据,而是从输入缓冲区中读数据。
这些I/O缓冲区特性可整理如下:
- I/O缓冲区在每个TCP套接字中单独存在;
- I/O缓冲区在创建套接字时自动生成;
- 即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
- 关闭套接字将丢失输入缓冲区中的数据。
输入输出缓冲区的默认大小一般都是 8K,可以通过 getsockopt() 函数获取:
unsigned optVal;
int optLen = sizeof(int);
getsockopt(servSock, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen);
printf("Buffer length: %d\n", optVal);
运行结果:Buffer length: 8192
阻塞模式
对于TCP套接字(默认情况下),当使用 write()/send() 发送数据时:
首先会检查缓冲区,如果缓冲区的可用空间长度小于要发送的数据,那么 write()/send() 会被阻塞(暂停执行),直到缓冲区中的数据被发送到目标机器,腾出足够的空间,才唤醒 write()/send() 函数继续写入数据。
如果TCP协议正在向网络发送数据,那么输出缓冲区会被锁定,不允许写入,write()/send() 也会被阻塞,直到数据发送完毕缓冲区解锁,write()/send() 才会被唤醒。
如果要写入的数据大于缓冲区的最大长度,那么将分批写入。
直到所有数据被写入缓冲区 write()/send() 才能返回。
当使用 read()/recv() 读取数据时:
首先会检查缓冲区,如果缓冲区中有数据,那么就读取,否则函数会被阻塞,直到网络上有数据到来。
如果要读取的数据长度小于缓冲区中的数据长度,那么就不能一次性将缓冲区中的所有数据读出,剩余数据将不断积压,直到有 read()/recv() 函数再次读取。
直到读取到数据后 read()/recv() 函数才会返回,否则就一直被阻塞。
这就是TCP套接字的阻塞模式。所谓阻塞,就是上一步动作没有完成,下一步动作将暂停,直到上一步动作完成后才能继续,以保持同步性。