IO是干嘛的?
IO是电脑对数据的读入和输出,I和O是相对于本机的电脑内存而言。如果是java的程序那么就是操作系统为java程序进程分配的系统内存,用于存储此应用的需要的上下文信息(这个程序需要存储的数据信息)。
我们要把数据读入到内存就是InputStream,这个过程涉及两个端点,一个是数据的源头,也就是数据源,数据源可以是本机的硬盘、也可以是内存的另一块地址。InputStream干的就是把这个数据源的数据读取到它指定的内存地址位置,这个过程会涉及到操作系统。OutputStream刚好相反就是把内存的数据拷贝到内存的另一个地址、或者写入到硬盘的具体位置。
思考:
既然IO可以进行数据的输入输出操作,内存既可以是数据源也可以是数据接收方,那么这个时候如果是两台计算机,那么我们就可以实现两个计算机的内存的数据相互收发。所以网络编程的本质就是IO操作。
深入思考:
现在我们有一个服务器和一个客户端,两个程序要相互通信。首先服务端会启动起来,监听本机的一个端口号比如8888,这个时候再启动客户端客户端会绑定这个服务器的IP和8888端口,这个时候就可以通过网络找到网络中的这台服务器。
IP是网络中一台主机的唯一标识,所以这个消息可以通过IP找到网络中唯一的服务器,到了服务器,进入网卡、进入内存,这个时候就会切去消息的IP头部,然后被操作系统内核读入对应的内存,操作系统内核会为每一个应用进程分配一个端口号和内存和cpu执行权等资源。 因为我们的服务器已经绑定了8888端口,操作系统内核的内存有这个数据之后,操作系统内核就可以通过消息的TCP头部就能获得服务器的端口8888,操作系统就会通过这个端口号知道这个数据应该给哪个进程,如果是8888这个进程就会把这个数据给复制到8888这个进程对应的内存当中。需要注意这个过程中服务器是一直在它的进程对应的这块内存等待是否有数据写入,有就读进来,然后对数据消息进行转发或者其它操作。
BIO(阻塞式IO):
服务器的InputStream.read()这个方法一直在内存区域等待数据输入,代码无法进行下一行,这个时候整个进程就阻塞了。这就是BIO。也就是程序等待数据从网络输入本机,进入操作系统内核对应的内存,通过操作系统内核把数据给复制到进程对应的内存中,这个方法会死等这个数据的写入,干不了其它事,如果是单线程那么就只能和一个客户端进行通信。
NIO(非阻塞式IO):
服务器的读数据的方法不会一直在进程对应的内存中等待,而是过一段时间询问进程对应的内存是否有新的数据复制进来,如果没有等待操作系统内核把数据复制到进程区域的内存,它需要我们主动每隔一端时间进行对内存数据准备区进行询问,直到有一次我们问它,刚好它有数据了,我们的方法就可以返回具体的数据,没有也会方法也会立马返回,不会阻塞。
IO的多路复用,我们需要不断地主动询问数据是否准备好,所以这个过程虽然式非阻塞的,但是是同步的,我们虽然不会一直等,但是也干不了其它事,但是我们可以和多个客户端进行通信,这个时候我们只需要实现一个IO多路复用器,让它去询问不同客户端的输入,监视它们对应的内存的不同地址,如果内存有数据就可以读取进来。这样就可以实现一个线程多个用户向服务器发送消息。
AIO:
异步非阻塞IO, 服务器端会调用方法询问内存是否有客户端写入数据,如果有就返回数据,如果没有就马上返回空,然后这个方法就不需要再去询问内存中数据是否准备好,如果准备好了需要操作系统内核主动的去把数据复制到进程的内存,然后告诉程序你要的数据准备好了,这个过程是异步非阻塞的,进程可以去干其它事情。数据的返回是通过回调函数完成的。
补充:
同步/异步/阻塞/非阻塞:
同步和异步:是描述通信机制的,是这个调用一直等到通信结果如果是等到结果返回才返回是同步,如果是调用马上就可以返回,但是结果要等到后面才能返回那么就是异步的。
阻塞和非阻塞:描述的是等待调用结果的进程的状态,如果一个线程等待一个返回结果什么都做不了,这就是阻塞。如果一个线程在等待一个返回结果的同时可以做其它事,这就是非阻塞。