SOCKET(三)阻塞式与非阻塞式

发布时间 2023-04-21 00:00:59作者: Aspirants

如果你运行过(一)中的范例,你会发现,客户端与服务端未建立连接前会在accpet与connect之间等待,建立后进行读写时,不论是客户端还是服务端都会在read()处等待另一方发送。本节让我们来了解以下他们的阻塞等待原理。

阻塞机制

connnect()

客户端通过connect()主动向服务端建立连接,并等待服务端的反馈,实现三次握手。范例(一)中,我们的服务端都是稳定运行的,一旦客户端发起连接,就会快速响应,connect也随之会快速返回结果给客户。但如果listen()中监听队列已满,服务器迟迟不反馈给客户端,那connet()又会如何让处理呢?我们说过,connect()使一个发起三次握手阻塞函数,调用它会每隔一段时间发起一个SYN包,直至服务器反馈,若超过一定时间仍未收到服务器反馈则会显示响应超时。

accpet()

上节我们说过,connect与listen交互建立连接后,产生套接字会放入一个队列中,供accept取用。accept作用就是查看该队列是否有新生成的套接字,若有则立即返回,无则阻塞等待。

read()

read()的阻塞机制在于调用它时会询问内核缓存是否有数据可读入,若无则会等待内核缓存准备好数据直至递送给read()

非阻塞实现

我们可以看出,这些阻塞函数会一直等待结果,在阻塞期间程序会不能做其他的事,这样会使我们的时间利用率降低,因此我们可以将他们设置未非阻塞,在未等到结果时可以让程序做其他的事。

实现原理

轮子阻塞还是非阻塞的属性其实与函数本身无关,而是取决于放入的套接字,默认情况下,socket是阻塞式的,因此只需要改变socket本身的属性,就可以实现轮子的非阻塞调用了。

设置为非阻塞

1.创建时添入SOCK_NONBLOCK设置

int socket(int domain, int type, int protocol);
int s = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);

2.使用fcntl函数设置

fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL, 0) | O_NONBLOCK);

3.使用ioctl函数设置

ioctl(sockfd, FIONBIO, 1); //1:非阻塞 0:阻塞

当我们将套接字设置为非阻塞后,accept与read会进行查询,若无结果或出错都会迅速返回。而connect较为特殊,如果我们调用非阻塞connect后,它会发送一个SYN包而迅速返回,那么之后的三次握手是否建立成功,就需要我们使用poll函数检测套接字的状态是否可写,监测超时时间可以由我们自己决定。