nio
同步、异步、阻塞、非阻塞。
假设一个线程在执行一个io操作,这个io操作需要一定的时候会有数据返回给该线程。
阻塞:如果该线程在等待io返回数据,什么也不做,直到有数据返回,则就是阻塞。
非阻塞如果该线程提交了io操作请求后,去干其他的事情,不等待结果,就是非阻塞。
阻塞非阻塞是指该线程是否卡在这里了。
io操作返回数据如何交给该线程?
同步:如果是该线程不停的询问io是否操作完毕有数据返回,则是同步
异步:如果该线程不去问,而是当io操作完了主动通知该线程有消息了,则是异步
同步异步是指如果获取到回执消息。
bio最基本的同步阻塞,nio基于多路复用实现的同步非阻塞,aio异步非阻塞。
客户端与服务端进行网络通信通常是客户端创建一个socket,服务端创建一个serversocket,然后把这两个socket连接到一起(也就是创建了一个连接),然后服务端开启监听来监听客户端是否有请求过来,如果采用bio的方式,则需要为每一个客户端的连接都创建一个线程(即使没有请求也要创建线程),并发量大的时候服务端需要大量线程,服务端承受不住,nio用来解决这个问题。
nio由selector负责对客户端socket进行监听,selector底层是linux的epoll函数实现。
epoll
- 文件列表fd:代表linux文件系统,用来存储所有涉及的数据
- socket队列:数据结构为红黑树,用来存储socket,每个socket代表一个客户端连接,每个socket都持有eventPoll的引用
- 工作队列-进程A、B:可以理解成当前运行的进程(线程),CPU挨个执行他们
- rdlist:链表结构,用来存储就绪状态的socket(有真正数据过来的socket)
- eventPoll:工作进程和socket的中间层,rdlist是它的一个成员,还有一个队列用来存储进程(进程A)
int s = socket(AF_INET, SOCK_STREAM, 0);
bind(s, ...)
listen(s, ...)
int epfd = epoll_create(...);
epoll_ctl(epfd, ...); //将所有需要监听的socket添加到epfd中
while(1){
int n = epoll_wait(...)
for(接收到数据的socket){
//处理
}
}
- 执行epoll_create函数:cpu创建eventpoll对象
- 调用epoll_ctl函数:cpu添加socket到文件系统,同时将eventpoll设置到socket中
- 执行epoll_wait:将进程A添加到eventpoll的队列并且阻塞。
- 当socket有真正数据过来的时候,cpu通过该socket的eventpoll引用,将socket添加到eventpoll的rdlist中。同时cpu会唤醒进程A,进程A就会查询rdlist,在rdlist中发现有就绪的socket,直接带着socket进入工作队列等待cpu调用执行