您的位置 首页 java

揭开网络编程的神秘面纱1(BIO、伪异步IO、NIO、AIO)

什么是 socket ?

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为socket。socket编程进行的是端到端的通信,基于 网络层 和传输层的实现。在网络层,socket函数需要指定底层是使用 IPV4 还是IPV6协议,传输层需要指定是 tcp 还是udp。

基于TCP协议的socket调用过程

基于UDP协议的socket调用过程

java 中的socket

java中的socket可以分为普通Socket和NioSocket两种。

普通Socket分为ServerSocket和Socket两大类,ServerSocket用于服务端,通过 accept 方法监听请求,监听到请求后返回Socket,Socket用于完成数据传输,客户端直接使用Socket发起请求并传输数据。

NioSocket是JDK1.4新增加的IO模式,底层采用新的处理方式,大大提高java IO的效率。Socket也属于IO的一种,NioSocket提供相应的类:ServerSocketChannel和SocketChannel,分别对应Socket中的ServerSocket和Socket。

BIO 编程(同步阻塞IO)

阻塞和非阻塞

阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作方法的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入方法会立即返回一个状态值。

采用BIO通信模型的服务端,通常由一个独立的Acceptor 线程 负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成后,通过输出流返回应答给客户端,线程销毁。

BIO模型,服务器实现模式为一个连接一个线程,也就是客户端有连接请求时服务端就需要启动一个线程来进行处理,如果这个连接不做任何事情会造成不必要的线程开销。

ServerSocket server = new ServerSocket(portNumber); //创建一个新的ServerSocket,用以监听指定端口上的连接请求
......
Socket clientSocket;
while(true){
clientSocket = serverSocket.accept(); //对accept()方法的调用将被阻塞,直到一个连接建立
 
 new Thread(new ServerHandler(clientSocket)).start(); //创建线程,处理clientSocket链路上的数据
} 

ServerSocket上的accept()方法将会一直阻塞,直到一个连接建立,随后返回一个新的Socket用于客户端和服务端之间的通信。ServerSocket将继续监听传入的连接。

当客户端并发访问量增加后,服务端的线程个数和客户端并发数将以1:1的关系增长,由于线程是比较宝贵的系统资源,线程数量快速膨胀后,系统的性能将急剧下降,最终系统将挂掉。所以BIO模型只适用于连接数目比较小且固定的架构。

伪异步I/O编程

为了避免为每个客户端请求都创建一个独立的线程造成的线程资源耗尽问题,我们服务端把客户端的Socket请求封装成一个Task(实现java.lang.Runnable接口),放到 线程池 进行管理。线程池维护一个消息队列和N个活跃线程对消息队列中的任务进行处理。这种方式叫做伪异步I/O编程。

由于线程池可以设置消息对垒的大小和最大线程数,因此,它的资源占用是可控制的。

ServerSocket server = new ServerSocket(portNumber); //创建一个新的ServerSocket,用以监听指定端口上的连接请求

ExecutorService executorService = Executors.newFixedThreadPool(60); //创建线程池
......
Socket clientSocket;
while(true){
clientSocket = serverSocket.accept(); //对accept()方法的调用将被阻塞,直到一个连接建立
 
 executorService.execute(new ServerHandler(clientSocket));//把clientSocket请求提交给线程池,在线程池中处理clientSocket链路上的数据
} 

我们可以根据需求,选择合适的线程池。如果使用CachedThreadPool线程池,除了能自动帮我们管理线程(线程复用),客户端与线程数也是1:1的模型。而如果使用FixedThreadPool线程池,我们可以有效控制线程的最大数量,保证系统线程资源的控制,实现N:M的伪异步IO模型,由此引入的问题也一目了然,由于限制了线程数量,如果发生大量客户端请求时并且超过最大线程数,只能把请求放到队列中等待,直到线程池中有空闲的线程可以复用。这也就会导致Socket一直阻塞。

如果对线程池不够了解的同学,可以查阅我的文章《 》。

下文讲解NIO、AIO编程。

文章来源:智云一二三科技

文章标题:揭开网络编程的神秘面纱1(BIO、伪异步IO、NIO、AIO)

文章地址:https://www.zhihuclub.com/182187.shtml

关于作者: 智云科技

热门文章

网站地图