高级Java工程师必备 ----- 深入分析 Java IO (一)BIO

BIO编程 最原始BIO

网络编程的基本模型是C/S模型,即两个进程间的通信。

服务端提供IP和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信。

传统的同步阻塞模型开发中,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
最原始BIO通信模型图:

高级Java工程师必备 ----- 深入分析 Java IO (一)BIO

存在的问题:

同一时间,服务器只能接受来自于客户端A的请求信息;虽然客户端A和客户端B的请求是同时进行的,但客户端B发送的请求信息只能等到服务器接受完A的请求数据后,才能被接受。(acceptor只有在接受完client1的请求后才能接受client2的请求)

由于服务器一次只能处理一个客户端请求,当处理完成并返回后(或者异常时),才能进行第二次请求的处理。很显然,这样的处理方式在高并发的情况下,是不能采用的。

一请求一线程BIO

那有没有方法改进呢? ,答案是有的。改进后BIO通信模型图:

高级Java工程师必备 ----- 深入分析 Java IO (一)BIO

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

代码演示

服务端:

package demo.com.test.io.bio; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import demo.com.test.io.nio.NioSocketServer; public class BioSocketServer { //默认的端口号 private static int DEFAULT_PORT = 8083; public static void main(String[] args) { ServerSocket serverSocket = null; try { System.out.println("监听来自于"+DEFAULT_PORT+"的端口信息"); serverSocket = new ServerSocket(DEFAULT_PORT); while(true) { Socket socket = serverSocket.accept(); SocketServerThread socketServerThread = new SocketServerThread(socket); new Thread(socketServerThread).start(); } } catch(Exception e) { } finally { if(serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } //这个wait不涉及到具体的实验逻辑,只是为了保证守护线程在启动所有线程后,进入等待状态 synchronized (NioSocketServer.class) { try { BioSocketServer.class.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class SocketServerThread implements Runnable { private Socket socket; public SocketServerThread (Socket socket) { this.socket = socket; } @Override public void run() { InputStream in = null; OutputStream out = null; try { //下面我们收取信息 in = socket.getInputStream(); out = socket.getOutputStream(); Integer sourcePort = socket.getPort(); int maxLen = 1024; byte[] contextBytes = new byte[maxLen]; //使用线程,同样无法解决read方法的阻塞问题, //也就是说read方法处同样会被阻塞,直到操作系统有数据准备好 int realLen = in.read(contextBytes, 0, maxLen); //读取信息 String message = new String(contextBytes , 0 , realLen); //下面打印信息 System.out.println("服务器收到来自于端口:" + sourcePort + "的信息:" + message); //下面开始发送信息 out.write("回发响应信息!".getBytes()); } catch(Exception e) { System.out.println(e.getMessage()); } finally { //试图关闭 try { if(in != null) { in.close(); } if(out != null) { out.close(); } if(this.socket != null) { this.socket.close(); } } catch (IOException e) { System.out.println(e.getMessage()); } } } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpxyfs.html