recv 和 send 阻塞和非阻塞的区别和联系
目录
答案
- 阻塞,事情干不完就不要回来了!
- 非阻塞,能干多少就是多少,赶紧回来!
深入说明
// 将内核接收缓冲区中的数据 copy 到应用层中用户的 buffer 中。
int recv(int sockfd, void *buf, size_t len, int flag);
// 将应用层中用户的 buffer 中的数据 copy 到内核发送缓冲区中。
int send(int sockfd, void *buf, size_t len, int flag);
1、recv
阻塞模式下,如果内核的接收缓冲区中没有数据时,该函数就会阻塞。
非阻塞模式下,如果内核的接收缓冲区中没有数据时,该函数就会返回。
2、send
阻塞模式下,如果发送缓冲区剩余的空间小于要发送的数据的大小,那么该函数会阻塞。
非阻塞模式下,如果发送缓冲区剩余的空间大于要发送的数据的大小,那么该函数会返回。
在 epoll 中的应用
在 epoll 中,“可读” 和 “可写” 分别对应的选项是 “EPOLLIN” 和 “EPOLLOUT”。
1、水平触发模式(LT)
在该模式下,recv 和 send 为阻塞和非阻塞,结果是一样的。
举 recv 栗子来说,因为在该触发模式下,只要内核的接收缓冲区中有数据,epoll_wait() 函数都会返回,这就导致了虽然 recv 是阻塞模式的,但是每次调用时,内核缓冲区都是有数据的,所以不会导致 recv 阻塞。当然了,当 recv 时是阻塞的,就更不会造成阻塞情况了。同理,对于 send 也是一样的。
2、边沿触发模式(LT)
在该模式下,recv 和 send 需要为非阻塞模式,不然就会有问题。
还是举 recv 栗子来说,因为在该模式下,内核的接收缓冲区来数据了,那么 epoll_wait() 函数就会返回,但是仅仅返回一次,它可不管你是否在该次中是否完全取走了内核接收缓冲区中的数据。
在上述的前提下,recv 需要一个 while (true) 循环,保证将缓冲区中的数据取空。这就产生了一个问题,因为假如 recv 是阻塞的,那么当内核缓冲区中没有数据时,该函数就会阻塞,这是致命的,所以 recv 必须是非阻塞的。
同理,对于 send 也是一样的。理想情况时,终于有机会向对端发送数据了,一定要将想发的数据发尽。这就有问题了,如果是阻塞的,如果发到一半,发送缓冲区满了,那么该函数就阻塞了,实际上要避免这种情况,所以要将该 send 设置为非阻塞的。
总结
-
在水平触发模式下,recv 和 send 阻塞和非阻塞模式均可。
-
在边沿触发模式下,recv 和 send 必须为非阻塞模式。
(SAW:Game Over!)