文章目录
- 阻塞式和非阻塞式IO
本篇总结的核心内容就是非阻塞式IO,直接看代码
阻塞式和非阻塞式IO
阻塞式IO
如下所示是典型的阻塞式IO
#include <iostream>
#include <unistd.h>
using namespace std;
int main()
{
char buff[1024];
while(true)
{
ssize_t n = read(0, buff, sizeof(buff) - 1);
// 如果接收成功
if(n > 0)
{
buff[n - 1] = 0;
cout << "buffer get : " << buff << endl;
}
// 如果结束
else if(n == 0)
{
cout << "read done" << endl;
break;
}
else
{
cerr << "read error" << endl;
break;
}
}
return 0;
}
非阻塞式IO
那如果今天我们想把他更换为非阻塞式IO呢?就要用到前面所说的一个fcntl函数了:
这个函数的第二个参数是一个标记位,里面包含的是众多的common选项,当它获取成功之后,回返回一个当前文件的一个老的标记位,而如果想要设置的话就可以用这个老的标记位按位与进去一个新的,这样就设置好了所谓的非阻塞式标记位,下面用代码来实现:
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <cerrno>
#include <cstring>
using namespace std;
void SetNonblock(int fd)
{
int fl = fcntl(fd, F_GETFL);
if (fl < 0)
{
cerr << "fcntl" << endl;
return;
}
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
cout << "set " << fd << " nonblock success" << endl;
}
int main()
{
char buff[1024];
SetNonblock(0);
while (true)
{
ssize_t n = read(0, buff, sizeof(buff) - 1);
// 如果接收成功
if (n > 0)
{
buff[n - 1] = 0;
cout << "buffer get : " << buff << endl;
}
// 如果结束
else if (n == 0)
{
cout << "read done" << endl;
break;
}
else
{
cerr << "read error"
<< ", n : " << n << ", error : " << errno << " , strerror : " << strerror(errno) << endl;
sleep(1);
}
}
return 0;
}
由运行结果可以看出,直接运行失败了,返回值是-1,并且在错误码的信息中可以看到是资源没有就绪
上图所示的就是非阻塞式轮询了,由于用户输入的数据太慢了,所以在实际的查询中,绝大多数情况下都是查询不到消息的,而由此得出的一个重要结论是,如果被设置为非阻塞式轮询,那么如果底层的fd对应的数据没有就绪,那么像这样的recvfrom或者是read的接口,返回值就会以出错的形式进行返回
但是实际上,这真的出错了吗?答案必然是没有的,出错是分情况的,一种是真的出错了,比如这个文件描述符被关闭了,或者是其他的意外情况,但是也可能是因为资源没有就绪的情况,所以避免的方式就是通过error错误码来进行区分,由此可以看出的一点是,非阻塞式是会进行轮询的检查的,并且不会阻塞在这个函数这里,而是会一直的去检查对应的信息,如果资源没有就绪,还可以去做其他的事,等资源就绪了再进行一些其他的操作
阻塞式和非阻塞式IO整体来说比较简单,重点是下面的话题,多路转接