2015/12/03

socket缓冲区以及阻塞模式


socket缓冲区

每个 socket 创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

write()/send() 并不会立刻向网络中传输数据,而是先将数据写入缓冲区,再由TCP协议将数据从缓冲区发送到目标机器。一旦数据写入到缓冲区,函数就可以成功返回。不管它们有没有到达 目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。

read()/recv() 函数也是如此,从输入缓冲区中读取数据,而不是直接从网络中读取。

/blog/img/huanchong_01.jpg


这些I/O缓冲区特性可以整理如下:

I/O缓冲区在每个TCP套接字中单独存在;

I/O缓冲区在创建套接字时自动生成;

即使关闭套接字也会继续传送输出缓冲区中遗留的数据;

关闭套接字将丢失输入缓冲区中的数据。

我们可以通过 getsockopt() 来查看输入输出缓冲区的大小

int sz;
socklen_t len = sizeof(int);
getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (void *)&sz, &len);
printf("read cache size:%d\n", sz);

getsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&sz, &len);
printf("write cache size:%d\n", sz);

输出

read cache size:1061808
write cache size:2626560

socket阻塞模式

对于TCP套接字,默认情况下是属于阻塞模式,当使用 write() / send() 发送数据时:

1.首先会检查缓冲区,如果缓冲区的可用空间长度 小于 要发送的数据,那么 write() / send() 会被阻塞,直到缓冲区中的数据被发送到目标机器,腾出足够的空间,才唤醒 write() / send() 函数 继续写入数据。

2.如果TCP协议正在向网络发送数据,那么输出缓冲区会被锁定,不允许写入, write() / send() 会被阻塞,直到数据发送完毕缓冲区解锁, write() / send() 才会被唤醒。

3.如果要写入的数据大于缓冲区的最大长度,那么将分批写入,直到所有数据都写入缓冲区 write() / send() 才能返回。


当使用 read() / recv() 读取数据时:

1.首选检查输入缓冲区,如果缓冲区中有数据,那么就读取,否则函数会阻塞,直到网络有数据的到来。

2.如果要读取的数据长度 小于 缓冲区中的数据长度,那么就不能一次性将缓冲区中的所有数据读出,剩余数据将吧不断积压,直到下次 read() / recv() 函数再次读取。

3.read() / recv() 调用,一定要等到读取到数据才会返回,否则就一直被阻塞。