项目GitHub地址:https://github.com/scientist272/non-blocking-server
nio 与阻塞式io
nio即non-blocking io,在网络编程中,相对于传统的阻塞式io,nio只需要一个或者几个线程就能处理大量并发的socket请求,而传统的阻塞式io处理大量并发请求时,很多时候需要为每一个请求创建一条新线程,对于很多长时间保持socket连接但是不发送数据的请求,为它们申请大量线程会造成资源浪费。
传统阻塞式io:
服务器持续调用accept方法接收socket连接,如果当前没有连接,则一直阻塞到有请求connect到服务器为止。但是这种io会有问题,如图中,当一个请求连接到服务器后,如果它一直什么事都不做,服务器需要等待它的write方法发送数据,这时候会造成处理线程的阻塞,服务器无法处理其他socket的连接请求。
解决方法:为每一个socket请求创建一条处理线程
但是这种方法还是有很大的缺点:即之前提到的,如果有很多请求一直保持和服务器的连接,但却不做任何事,会造成大量的服务器资源浪费。
nio(非阻塞式io)
nio的解决思路:
服务器保持一个或几个线程,通过selector(选择器)来处理大量并发请求。
开启一个serverSocketChannel,将它注册到selector中,操作为SelectionKey.OP_ACCEPT,即监听接收就绪的socket(有socket连接服务器时,selector就会知道,之后可对它们就行读就绪注册,写就绪注册等)。调用selector的selectNow()方法,这是一个非阻塞的native方法,它会选择出当前所有可操作的socketChannel,包括acceptable,readable,writeable等,即接收就绪,读就绪,写就绪的通道。
简而言之,就是服务器在一个线程中循环调用selectNow()方法,当有socket请求连接服务器时,就对它就行注册,注册完之后就做其他事去了,比如处理其他之前注册的socket请求发送的数据,等这个新连接的socket有数据时,才对它进行处理。
实现思路
服务器用两个线程来工作,一个线程负责接收新的socket,然后将它们加入到一个工作队列中,另一个负责处理socket的线程从工作队列中获取socket并对它们进行注册。
处理线程会对每一个注册的socket创建一个messageReader实例来读取它们的数据并解析,一个messageWriter负责服务器向它们返回数据。整个处理流程如下图所示
对请求的数据存储用了一个可变的缓冲区数据结构,其中的byte数组用于存储数据,循环队列用于记录byte数组可用的数据块。
具体实现见GitHub代码,有详细注释。