IO、NIO、AIO
Socket
在应用程序中通常使用套接字来向网络发出请求或者应答网络请求。
Socket(套接字):用于唯一标识网络上的某台主机上的某个进程(服务),Socket就是IP:Port的形式,一个IP地址和一个端口号来标识。
Java中Socket和ServerSocket类库位于Java.net包中,ServerSocket用于服务器端,Socket则在建立网络链接时使用,也就是客户端,在连接完成后应用程序两端都会产生Socket实例。注意:虽然有客户端和服务端的概念,但是对于网络连接来说,套接字是平等的。
建立网络连接:
- 服务器监听:服务端打开端口,用于监听连接请求。
- 客户端请求:客户端使用套接字来请求连接。
- 服务端确认:监听到连接请求后,响应客户端的请求,并建立一个新的进程用于响应,然后将服务端的套接字发送回客户端。
- 一旦客户端确认了,就建立好连接,双方开始通信。客户端仍然处于监听状态。
网络编程的基于模型Client/Server模型,三次握手,基于TCP的连接。
IO
直接使用Socket建立连接,进行通信的方式就是BIO(阻塞式IO)在建立TCP响应的时候,对于服务端端口是阻塞的。当有一个客户端进行访问时,其他的客户端是不能访问的。
优化方案:服务端使用线程池管理,伪异步。但由于使用的是阻塞队列,所以是伪异步的。
public class Server {
final static int PORT = 8765;
public static void main(String[] args) {
ServerSocket server = null;
BufferedReader in = null;
PrintWriter out = null;
try {
server = new ServerSocket(PORT);
System.out.println("server start");
Socket socket = null;
HandlerExecutorPool executorPool = new HandlerExecutorPool(50, 1000);
while(true){
socket = server.accept();
//使用线程池进行操作实现异步
executorPool.execute(new ServerHandler(socket));
}
} catch (Exception e) {
e.printStackTrace();
}
NIO
NIO有两种说法new IO和Not—block IO。习惯于非阻塞IO。注意NIO只是非阻塞但不是异步的。
本质就是避免了TCP连接的3次握手操作,减少连接的开销。
核心是通道和多路复用器:
通道:双向的通信方式,具有读写的能力,同时具有状态位。
多路复用器:也就是seletor。提供轮询管理通道的能力。所有通道都由selector管理。每一个在复用器上注册了的管道都会被分配一个key用作标识,复用器也会通过key去找到相应的管道。
每个管道具有的状态码:
SelectionKey.OP_CONNECT 连接状态
SelectionKey.OP_ACCEPT 阻塞状态
SelectionKey.OP_READ 可读状态
SelectionKey.OP_WRITE 可写状态
步骤:
创建NIO服务器的主要步骤
(1)打开ServerSocketChannel,监听客户端连接
(2)绑定监听端口,设置连接为非阻塞模式
(3)创建Reactor线程,创建多路复用器并启动线程
(4)将ServerSocketChannel注册到Reactor线程中的Selector上,监听ACCEPT事件
(5)Selector轮询准备就绪的key
(6)Selector监听到新的客户端接入,处理新的接入请求,完成TCP三次握手,简历物理链路
(7)设置客户端链路为非阻塞模式
(8)将新接入的客户端连接注册到Reactor线程的Selector上,监听读操作,读取客户端发送的网络消息
(9)异步读取客户端消息到缓冲区
(10)对Buffer编解码,处理半包消息,将解码成功的消息封装成Task
(11)将应答消息编码为Buffer,调用SocketChannel的write将消息异步发送给客户端
AIO
AIO编程,在NIO基础上引入了异步通道的概念,提供异步文件和异步套接字通道的实现,从而在真正意义上实现了异步非阻塞。核心是AsynchronousServerScoketChannel和AsynchronousScoketChanel
核心:
//客户端:
executorService = Executors.newCachedThreadPool();
//创建线程组
threadGroup = AsynchronousChannelGroup.withCachedThreadPool(executorService, 1);
//创建服务器通道
assc = AsynchronousServerSocketChannel.open(threadGroup);
//进行绑定
assc.bind(new InetSocketAddress(port));
System.out.println("server start , port : " + port);
//进行阻塞
assc.accept(this, new ServerCompletionHandler());
//一直阻塞 不让服务器停止
Thread.sleep(Integer.MAX_VALUE);
//handler
public void completed(AsynchronousSocketChannel asc, Server attachment) {
//当有下一个客户端接入的时候 直接调用Server的accept方法,这样反复执行下去,保证多个客户端都可以阻塞(没有递归上限),1.7以后AIO才实现了异步非阻塞
attachment.assc.accept(attachment, this);
read(asc);
}