持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
- 英文小册原文地址:beej.us/guide/bgnet…
- 作者:Beej
- 中文翻译地址:www.chanmufeng.com/posts/netwo…
这并不是特别高阶的知识,但是可以让你跳脱出你已经掌握的基础知识了。如果你一步一步学习到这里了,你可以认为你已经相当精通Unix网络编程的基础知识了,恭喜你。
现在我们将进入一个新的世界,学习一下关于socket更深奥的知识。
你可能听说过阻塞(Blocking)这个词,那么它到底是个什么鬼东西?简而言之,“block”是“sleep”的一种更具有科技感的叫法。
下文中,「blocking」、「block」和「阻塞」会混用,都是一个意思,悉知
当你运行前文的listener程序时,你可能发现它一直在“block”,直到数据包到达才会动起来。产生这个现象的原因是它调用了recvfrom(),如果没有数据,recvfrom()就会被“block”(你可以理解为“sleep”),直到有数据到达。
很多函数都会block。accept()会block,所有的recv()会block,它们之所以可以block,是因为它们被内核赋予了这个能力。当你使用socket()函数创建出一个socket descriptor时,内核就会将其设置为blocking。如果你不想要blocking socket,你需要调用fcntl()进行设置:
#include <unistd.h>
#include <fcntl.h>
.
.
.
sockfd = socket(PF_INET, SOCK_STREAM, 0);
fcntl(sockfd, F_SETFL, O_NONBLOCK);
.
.
.
将socket设置为non-blocking(非阻塞),你就可以poll(轮询)socket来获取数据了。如果你对一个non-blocking socket进行读取,而socket没有数据的时候,它并不会阻塞,而是会返回-1,并且将errno设置为 EAGAIN 或 EWOULDBLOCK。
啥? EAGAIN 或 EWOULDBLOCK吗?那我到底应该检查哪一个?实际上,不同操作系统返回哪个值是不确定的,因此为了兼容性,你最好把这两个值都检查一下。
但是老实说,你要是时时刻刻进行数据轮询并不是个好主意,因为会空耗CPU时间片,做的大部分都是无用功。协下一节的poll()提供了一个更优雅的解决方案,用于检查是否有等待读取的数据。