网络I/O模型--01阻塞模式(普通)

很长一段时间内,大多数网络通信方式都是阻塞模式,即:
· 客户端 向服务器端发出请求后,客户端会一直处于等待状态(不会再做其他事情),直到服务器端返回结果或者网络出现问题 。
· 服务器端同样如此,当在处理某个客户端 A 发来的请求时,另 一个客户端 B 发来的请求会等待,直到服务器端的处理线程完成上一个请求的处理。

 

image

 

     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

 

posted @ 2018-05-12 20:18  ParamousGIS  阅读(322)  评论(0编辑  收藏  举报