站酷网站,做vr网站,怎样将建设银行网站加入可信站,wordpress每页显示数量文章目录 前言一、案例工程二、交互流程2.1、服务端流程2.2、客户端流程2.3、 通信过程的详细时序图 三、多线程改进版总结 前言 BIO是阻塞的IO#xff0c;阻塞主要是体现在#xff1a;
服务端等待接受客户端的连接。 如果没有客户端连接到达#xff0c;调用线程会一直挂起… 文章目录 前言一、案例工程二、交互流程2.1、服务端流程2.2、客户端流程2.3、 通信过程的详细时序图 三、多线程改进版总结 前言 BIO是阻塞的IO阻塞主要是体现在
服务端等待接受客户端的连接。 如果没有客户端连接到达调用线程会一直挂起直到有连接到达才返回。服务端等待客户端的请求消息。 如果没有数据可读或者写缓冲区未就绪线程会一直等待直到满足条件。
一、案例工程 客户端
public class BIOClient {public static void main(String[] args) throws IOException {Socket socket new Socket(localhost, 1848);OutputStream out socket.getOutputStream();InputStream in socket.getInputStream();out.write(Hello Server.getBytes());out.flush();byte[] buffer new byte[1024];int len in.read(buffer);System.out.println(Server Echo: new String(buffer, 0, len));socket.close();}
} 服务端
public class BIOServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket(1848);System.out.println(EchoServer started on port 1848);//阻塞Socket clientSocket serverSocket.accept();handleClient(clientSocket);}private static void handleClient(Socket clientSocket) {try(OutputStream out clientSocket.getOutputStream();InputStream in clientSocket.getInputStream()){byte[] bytes new byte[1024];int len;while ((len in.read(bytes))! -1){out.write(bytes, 0, len);out.flush(); // 回显}}catch (Exception e){System.out.println(Client disconnected);}}
}先启动服务端再启动客户端运行结果客户端 Server Echo: Hello Server 二、交互流程 下面是案例工程完整的交互流程
服务端启动并监听端口 1848。客户端连接服务端并发送一段数据“Hello Server”。服务端接收数据并将收到的数据原样回写给客户端回显。客户端接收服务端返回的数据并打印。客户端关闭连接服务端接收到 read -1 表示连接断开。
2.1、服务端流程 监听端口 1848 阻塞等待客户端连接accept
ServerSocket serverSocket new ServerSocket(1848);
Socket clientSocket serverSocket.accept(); // 阻塞直到有客户端连接获取输入流/输出流阻塞读取客户端发来的数据收到后立即回写
InputStream in clientSocket.getInputStream();
int len in.read(bytes); // 阻塞直到有数据
out.write(bytes, 0, len); // 回显发送2.2、客户端流程 主动连接服务端并且获取输入输出流
Socket socket new Socket(localhost, 1848);
OutputStream out socket.getOutputStream();
InputStream in socket.getInputStream();向服务端发送字符串 “Hello Server”
out.write(Hello Server.getBytes());读取服务端的回显会阻塞等待。
byte[] buffer new byte[1024];
int len in.read(buffer);
System.out.println(Server Echo: new String(buffer, 0, len));关闭连接
socket.close();2.3、 通信过程的详细时序图
客户端 服务端| ||--------connect(1848)--------|| ||-----Hello Server----------|| ||-----Hello Server(echo)----|| ||----[关闭连接]----------------|| | 可以通过debug的方式加深一下关于阻塞的理解先启动服务端不启动客户端模拟没有客户端连接的情况代码阻塞在accept处 但是通过jstack命令是无法真正判断阻塞的上图中线程的状态还是Runnable。是因为accept底层调用的本地方法accept0 是 C 语言层面的调用阻塞在 epoll/poll/select 上等待连接JVM 只是调用了它并不知道它“卡”住了所以线程状态是 RUNNABLE。实际上这个线程已经阻塞在线程上下文切换之外OS 层面只是 JVM 没法反映出来。 启动客户端后服务端接收到连接解除阻塞继续向下运行 客户端没有发送消息服务端继续在read处阻塞**模拟服务端长时间没有接收到客户端的请求 客户端发送消息后服务端解除了阻塞同理服务端没有发出消息前客户端也会在read处阻塞模拟客户端长时间没有收到服务端的返回值**
三、多线程改进版 最初的案例工程服务端只能单线程地处理客户端的请求如果客户端有多个请求则需要依次执行所以在实际开发中会使用线程池的方式进行改进 服务端
public class BIOServer {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket(1848);System.out.println(EchoServer started on port 1848);while (true){//阻塞Socket clientSocket serverSocket.accept();handleClient(clientSocket);}}private static void handleClient(Socket clientSocket) {try(OutputStream out clientSocket.getOutputStream();InputStream in clientSocket.getInputStream()){byte[] bytes new byte[1024];int len;while ((len in.read(bytes))! -1){out.write(bytes, 0, len);out.flush(); // 回显}}catch (Exception e){System.out.println(Client disconnected);}}
}客户端
public class BIOClient {public static void main(String[] args) throws IOException {clientExec();clientExec();clientExec();clientExec();clientExec();}private static void clientExec() throws IOException {Socket socket new Socket(localhost, 1848);OutputStream out socket.getOutputStream();InputStream in socket.getInputStream();out.write((Hello Server: Thread.currentThread().getName()).getBytes());out.flush();byte[] buffer new byte[1024];int len in.read(buffer);System.out.println(Server Echo: new String(buffer, 0, len) 执行完成);socket.close();}
}运行结果服务端 New client connected,handler threadpool-1-thread-1 New client connected,handler threadpool-1-thread-2 New client connected,handler threadpool-1-thread-3 New client connected,handler threadpool-1-thread-4 New client connected,handler threadpool-1-thread-5 不难看出客户端的请求和服务端的处理在线程上是1:1的关系一个客户端的请求对应的一个服务端的线程去处理。也就是有多少个请求服务端就要开启多少个线程而线程的资源是有限的并且会存在cpu上下文切换的开销。服务器可能直接OOM或频繁GC无法处理高并发的场景。 并且BIO 中 in.read() 是阻塞操作如果客户端长时间不发数据线程会一直阻塞在那导致大量线程其实是「空转挂起」状态
总结 BIOBlocking I/O是阻塞式的网络通信模型在基于 TCP 协议的实现中阻塞主要体现在两个阶段
连接阶段ServerSocket.accept() 阻塞等待客户端连接通信阶段InputStream.read() 和 OutputStream.write() 阻塞等待数据读写。 在 BIO 模型中客户端每建立一个连接服务端就会创建一个独立线程进行处理客户端请求与服务端处理呈 1:1 的线程绑定关系。 「1:1线程-连接模型」是 BIO 的核心瓶颈当并发连接数上升时线程资源消耗激增系统调度压力大易导致性能下降或资源耗尽。它是一种比较原始的模型仅适用于学习或低并发业务场景在高并发生产环境中并不推荐使用。