网络I/O模型--01阻塞模式(普通)
很长一段时间内,大多数网络通信方式都是阻塞模式,即:
· 客户端 向服务器端发出请求后,客户端会一直处于等待状态(不会再做其他事情),直到服务器端返回结果或者网络出现问题 。
· 服务器端同样如此,当在处理某个客户端 A 发来的请求时,另 一个客户端 B 发来的请求会等待,直到服务器端的处理线程完成上一个请求的处理。
Java 对阻塞模式的支持,就是由 java. net 包中的 Socket 套接字功能完成的 。 这里要说明 一下 , Socket 套接字是 TCP/IP 等传输层协议在高级编程语言中的具体体现 。 例如客户端使用 TCP 协议连接这台服务器的时候,当 TCP 三次握手成功后,应用程序就会创建一个 Socket 套接字对象(注意,这时还没有进行数据内容的传输),当这个 TCP 连接出现数据传输时,Socket 套接字就会把数据传输的表现告诉程序员 。
客户端代码
package testBlockSocket; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.util.concurrent.CountDownLatch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SocketClientDaemon { public static void main(String[] args) throws Exception { Integer clientNumber = 20; CountDownLatch countDownLatch = new CountDownLatch(clientNumber); // 分别开始启动这 20 个客户端 for (int index = 0; index < clientNumber; index++, countDownLatch.countDown()) { SocketClientRequestThread client = new SocketClientRequestThread(countDownLatch, index); Thread thread = new Thread(client); thread.start(); } // 这个同步锁不涉及具体的实验逻辑,只是保证守护线程在启动所有线程后,不会退出 synchronized (SocketClientDaemon.class) { SocketClientDaemon.class.wait(); } } } class SocketClientRequestThread implements Runnable { private final static Logger LOGGER = LoggerFactory.getLogger(SocketClientRequestThread.class); private CountDownLatch countDownLatch; // 这个线程的编号 private Integer clientindex; // countDownLatch 是 Java 提供的线程同步计数器。 // 当计数器数值减为 0 时,所有受其影响而阻塞的线程将会被激活。尽可能模拟并发请求的真实性(但实际上也并不是完全并发的) public SocketClientRequestThread(CountDownLatch countDownLatch, Integer clientindex) { this.countDownLatch = countDownLatch; this.clientindex = clientindex; } @Override public void run() { Socket socket = null; OutputStream clientRequest = null; InputStream clientResponse = null; try { socket = new Socket("localhost", 8888); clientRequest = socket.getOutputStream(); clientResponse = socket.getInputStream(); // 阻塞,直到 SocketClientDaemon 完成所有线程的启动,然后所有线程一起发送请求 this.countDownLatch.await(); // 发送请求信息 clientRequest.write((" 这是第" + this.clientindex + " 个客户端的请求。").getBytes()); clientRequest.flush(); clientRequest.write((" 这是第" + this.clientindex + " 个客户端的 over ").getBytes()); clientRequest.flush(); // 在这里等待 , 直到服务器返回信息 SocketClientRequestThread.LOGGER.info("第" + this.clientindex + "个客户端的请求发送完成, 等待服务器返回信息"); int maxLen = 1024; byte[] contextBytes = new byte[maxLen]; int realLen; String message = ""; // 程序执行到这里 , 会一直等待服务器返回信息 // (注意,前键是 in 和 out 都不能关闭,如果关闭了就收不到) while ((realLen = clientResponse.read(contextBytes, 0, maxLen)) != -1) { message += new String(contextBytes, 0, realLen); SocketClientRequestThread.LOGGER.info("接收 到 来自服务器的信息 :" + message); } } catch (Exception e) { SocketClientRequestThread.LOGGER.error(e.getMessage(), e); } finally { // 记得关闭连接 try { clientRequest.close(); clientResponse.close(); socket.close(); } catch (Exception e) { SocketClientRequestThread.LOGGER.error(e.getMessage(), e); } } } }
服务端代码
package testBlockSocket; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SocketServer1 { private final static Logger LOGGER = LoggerFactory.getLogger(SocketServer1.class); public static void main(String[] args) throws Exception { ServerSocket serverSocket = new ServerSocket(8888); try { while (true) { // 这里 Java 通过 JNI 请求操作系统,并等待操作系统返回结果或出错 Socket socket = serverSocket.accept(); // 下面我们收取信息(这里还是阻塞式的, 一直等待 ,直到有数据可以接收 ) InputStream in = socket.getInputStream(); OutputStream out = socket.getOutputStream(); Integer sourcePort = socket.getPort(); int maxLen = 2048; byte[] contextBytes = new byte[maxLen]; int realLen; StringBuffer message = new StringBuffer(); // 试图读数据的时候,程序也会被阻塞,直到操作系统把网络传来的数据准备好 。 while ((realLen = in.read(contextBytes, 0, maxLen)) != -1) { message.append(new String(contextBytes, 0, realLen)); // 我们假设读取到"over"关键字表示一段内容传输完成 if (message.indexOf("over") != -1) { break; } } // 下面打印信息 LOGGER.info("服务器收到来自于端口 : " + sourcePort + "的信息:" + message); // 下面开始发送信息 out.write("回发响应信息 !".getBytes()); // 关闭 out.close(); in.close(); socket.close(); } } catch (Exception e) { SocketServer1.LOGGER.error(e.getMessage(), e); } finally { if (serverSocket != null) { serverSocket.close(); } } } }
完
java -classpath ./;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\slf4j-api-1.7.25.jar;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\log4j-api-2.10.0.jar;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\logback-core-1.2.3.jar;D:\Project\JavaWeb\HPArchitecture\BlockNIO\bin\logback-classic-1.2.3.jar testBlockSocket.SocketServerl