网络I/O
网络IO部分经常会被问到同步/异步、阻塞/非阻塞相关知识点,下面来总结一下我对这些知识点的理解。
网络IO有两个阶段第一个是数据就绪,第二阶段是数据读写。
数据就绪
数据就绪可以理解为数据有没有到达。那我们该怎么判断数据有没有到达呢?可以通过TCP接受缓冲区有没有数据到达来判断数据就绪,如果有数据那么就说明有数据到达了,如果TCP接受缓冲区没有数据那么说明没有数据到达。
数据就绪阶段又可以分为两个知识点,阻塞/非阻塞。
阻塞:调用了读写(I/O)方法的线程进入阻塞状态。
通俗的来说就是当我调用recv(read),而TCP缓冲区里没有数据时recv就会阻塞(recv默认是阻塞的)。
非阻塞:不会改变线程的状态,通过返回值来判断。
我们可以通过设置sockfd为O_NONBLOCK来设为非阻塞。那么可以通过它的返回值来判断,返回值如下:
int size = recv(sockfd, buf, 1024, 0);
size == -1 表示出错,没有读到数据或者是调用的系统函数失败了(EINTR, EAGAIN, EWOULDBLOCK)
size == 0 读取到数据的末尾,对方连接关闭
size > 0 读取到了多少数据,size就是读到的数据的长度了
EINTR:产生了信号吧函数中断以后会返回的错误号(如过有一个信号来,由于信号的优先级高就会去执行信号处理函数)
EAGAIN:没有读到数据,让我再读一次就会返回这两个错误号
数据读写
数据的读写也有两个知识点为同步/异步。
同步:首先初始化一个数组用来保存缓冲区里的内容,再用recv读数据,通过得到的size来判断独到的数据,数据就会通过应用程序从内核态读到用户态。这就是同步的概念。
优点:写起来简单易懂。
缺点:效率没有异步高,因为数据由应用程序搬,使用的时间是用户的时间。
char buf[1024] = {0};
int size = recv(sockfd, buf, 1024, 0);
...
if (size > 0) {
...
}
异步:还是需要一个数组来存储缓冲区里的内容,但是要存储的数据不需要由我们来进行搬运而是由内核帮我们搬,搬好以后就通知我们数据已经装好了我们再进行处理就可以了。
Linux异步接口:aio_read()/aio_write()
优点:内核自己帮我们搬数据,而我们可以继续往下的操作,搬好以后通知我们,我们再去处理数据。效率比同步高。
缺点:编程麻烦,调用异步接口然后执行其他内容,数据到了再去处理数据。