聊IO通信我们从最基本的也是最古老的BIO说起
什么是BIO
其实就是我们所讲的阻塞IO,他是由操作系统指令支持的。
BIO原理
如果要追踪系统调用需要用1.4版本的jdk编译运行,所以NIO就不追踪了,直接用学习时老师的图吧。
图片是通过strace命令来追踪系统调用时生成的out.*日志文件,这个命令是根据进程ID号来分文件的,我们只需要关注划红线的地方就好,当我们执行程序时需要生成Socket时就会从操作系统中执行Socket命令创建一个Socket,这个系统调用会返回一个文件描述符,可以理解为唯一标识Socket用的,然后我们看bind方法,bind方法会将我们前面生成的Socket bind到需要监听的端口号,然后Listen;
接下来我们看重点最后一行,发现了accept,明显这个方法是有空缺的,这就是BIO的表现,如果没有客户端连进来就会一直阻塞。我们尝试使用一个客户端去连接这个Socket。我们来看out文件的变化情况。
图中可以清楚的看到accept解除阻塞了,并且还可以看到连接的客户端是谁,端口号多少,然后返回一个文件描述符。现在有连接了我们需要进行读取连接发来的消息了。在我们的Java程序中我们需要从新开辟一个线程去读取数据。
我们看这条指令其实这条指令开辟的是一个轻量级进程,但是这个进程还是属于java主进程组的,上面说过strace命令是根据进程ID号来分文件的,所以我们把目光投入到对应的文件中。
我们看最后一行,这个应该都熟悉了,对于我们的read操作,是调用的内核的recv方法的,这个方法依然是阻塞的,这其实也是问什么程序中需要对每一个连接都要开辟一个线程的原因之一。不可能因为一个连接不发送数据而去阻塞主线程。
大致表现流程如下图。
优点:除了简单好像确实没了。
缺点:accept,recv会阻塞,浪费资源,每个客户端连接都需要开辟一个连接,当连接很多事,线程切换将会浪费很多资源。
这里涉及到cpu如何切换线程的,其实就是一个叫晶振器的硬件,过段时间就会给cpu发送一个高电平信号告诉cpu进行线程切换,线程切换过程会涉及到保护现场(也就是保存一下线程执行到哪里了),恢复现场的操作,如果线程太多,cpu的时间大部分都花在线程切换上了。
NIO闪亮登场
NIO就是一个非阻塞IO,当没有客户端连接时accept就会返回-1,recv也会返回-1,当有客户端连接时就会将客户端同一放在一个集合中,由主线程去遍历客户端集合进行读取。如果没有数据发送recv同样会返回-1,不会阻塞。内核调用指令还是一样,就不去追踪了,流程图大概是这样。