zoukankan      html  css  js  c++  java
  • 网络I/O模型--03非阻塞模式(ServerSocket与Socket的超时处理)--解除accept()、 read()方法阻塞

    对于阻塞方式的一种改进是在应用程序层面上将 “一直等待 ”的状态主动打开:

    这种模式下,应用程序的线程不再一直等待操作系统的 I/O状态,而是在等待一段时间后就解除阻塞。如果没有得到想要的结果,则再次进行相同的操作 。 这样的工作方式,保证了应用程序的线程不会一直阻塞,而可以进行一些其他工作一一例如软件业务层面上暂时不需要这些网络数据的操作过程

    image

    服务端代码(对accept()方法也解除阻塞)

    package testBlockSocket;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketTimeoutException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    //通过非阻塞的方式处理Socket连接
    public class SocketServer3SocketTimeout {
        private final static Logger LOGGER = LoggerFactory.getLogger(SocketServer3SocketTimeout.class);
        private static Object xWait = new Object();
    
        public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8888);
                // 设定阻塞时间
                serverSocket.setSoTimeout(100);
                while (true) {
                    Socket socket = null;
                    try {
                        // 程序不会一直在这里阻塞了
                        socket = serverSocket.accept();
                    } catch (SocketTimeoutException el) {
                        // 执行到这里,说明本次 accept ()方法没有接收到任何数据报文,主线程在这里就可以做一些事情,记为 x
                        synchronized (SocketServer3SocketTimeout.xWait) {
                            LOGGER.info("这次没有接收到 TCP 连接,据报文,等待 10 毫秒,模拟事件 x 的处理时间");
                            SocketServer3SocketTimeout.xWait.wait(10);
                        }
                        continue;
                    }
                    InputStream inputStream = socket.getInputStream();
                    OutputStream outputStream = socket.getOutputStream();
                    Integer sourcePort = socket.getPort();
                    int maxLen = 2048;
                    byte[] contextBytes = new byte[maxLen];
                    int realLen;
                    StringBuffer message = new StringBuffer();
                    // 以下接收数据,与 7.2.1 节中的代码处理过程一致
                    while ((realLen = inputStream.read(contextBytes, 0, maxLen)) != -1) {
                        message.append(new String(contextBytes, 0, realLen));
                        // 我们假设读取到"over"关键字表示一段内容传输完成
                        if (message.indexOf("over") != -1) {
                            break;
                        }
                    }
                    // 下面打印信息
                    LOGGER.info("服务器收到来自于端口 : " + sourcePort + "的信息:" + message);
                    // 下面开始发送信息
                    outputStream.write("回发响应信息 !".getBytes());
                    // 关闭
                    outputStream.close();
                    inputStream.close();
                    socket.close();
                }
            } catch (Exception e) {
                SocketServer3SocketTimeout.LOGGER.error(e.getMessage(), e);
            } finally {
                // 关闭连接
                if (serverSocket != null) {
                    serverSocket.close();
                }
            }
        }
    }

    服务端代码改进(对accept()和read()方法解除阻塞)

    package testBlockSocket;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketTimeoutException;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    //通过非阻塞的方式处理Socket连接
    //通过非阻塞的方式同时处理read()
    public class SocketServer3SocketTimeoutReadTimeout {
        private final static Logger LOGGER = LoggerFactory.getLogger(SocketServer3SocketTimeoutReadTimeout.class);
        private static Object xWait = new Object();
    
        public static void main(String[] args) throws IOException {
            ServerSocket serverSocket = null;
            try {
                serverSocket = new ServerSocket(8888);
                // 设定阻塞时间
                serverSocket.setSoTimeout(100);
                while (true) {
                    Socket socket = null;
                    try {
                        socket = serverSocket.accept();
                    } catch (SocketTimeoutException el) {
                        // ===================
                        // 执行到这里,说明本次 accept()方法没有接收到任何 TCP 连接主线程在这里就可以做一些事情,记为 x
                        // ==================
                        synchronized (SocketServer3SocketTimeoutReadTimeout.xWait) {
                            LOGGER.info("这次没有接收到 TCP 连接,等待10 毫秒,模拟事件x 的处理时间 ");
                            SocketServer3SocketTimeoutReadTimeout.xWait.wait(10);
                        }
                        continue;
                    }
                    InputStream inputStream = socket.getInputStream();
                    OutputStream outputStream = socket.getOutputStream();
                    Integer sourcePort = socket.getPort();
                    int maxLen = 2048;
                    byte[] contextBytes = new byte[maxLen];
                    int realLen;
                    StringBuffer message = new StringBuffer();
                    // 下面我们收取信息(非阻塞方式, read()方法的等待超时时间)
                    socket.setSoTimeout(10);
                    BIORead: while (true) {
                        try {
                            while ((realLen = inputStream.read(contextBytes, 0, maxLen)) != -1) {
                                message.append(new String(contextBytes, 0, realLen));
                                // 我们同样假设读取到"over"关键字,表示业务内容传输完成
                                if (message.indexOf("over") != -1) {
                                    break BIORead;
                                }
                            }
                        } catch (SocketTimeoutException e2) {
                            // =================
                            // 执行到这里,说明本次 read ()方法没有接收到任何数据流主线程在这里又可以做一些事情,记为 Y
                            // =================
                            LOGGER.info("这次没有接收到任务数据报文,等待 10 ~盖秒 ,模拟事件 Y 的处理时间 ");
                            continue;
                        }
                    }
                    // 下面打印信息
                    LOGGER.info("服务器收到来自子端口:" + sourcePort + "的信息:" + message);
                    // 下面开始发送信息
                    outputStream.write(" 回发响应信息 !".getBytes());
                    // 关闭in和 out 对象
                    inputStream.close();
                    outputStream.close();
                }
            } catch (Exception e) {
                LOGGER.error(e.getMessage(), e);
            } finally {
                // 关闭服务
                if (serverSocket != null) {
                    serverSocket.close();
                }
            }
        }
    }

          对阻塞模型的改进 : 让 TCP 连接和数据读取这两个过程,都变成了“非阻塞”方式 。

     

          这种方式对网络I/O 性能的提升意义不大,原因是这种处理方式实际上并没有解决accept()方法、 read()方法阻塞的根本问题 。 根据上文的描述, accept()方法、 read()方法阻塞的根本问题是底层接收数据时采用 了操作系统提供的“同步 I/O”工作方式。这两次改进过程,只是解决了I/O 操作的两步中的第一步:将程序层面的阻塞方式变成了非阻塞方式 。

  • 相关阅读:
    thinkphp5 tp5 命名空间 报错 Namespace declaration statement has to be the very first statement in the script
    开启 php 错误 提示 php-fpm 重启 nginx 500错误 解决办法 wdlinux lnmp 一键包 php脚本无法解析执行
    js 设置 cookie 定时 弹出层 提示层 下次访问 不再显示 弹窗 getCookie setCookie setTimeout
    php 二维数组 转字符串 implode 方便 mysql in 查询
    nginx 重启 ps -ef|grep nginx kill -HUP 主进程号
    jquery bootstrap help-block input 表单 提示 帮助 信息
    jquery 倒计时 60秒 短信 验证码 js ajax 获取
    jQuery如何获取同一个类标签的所有的值 遍历
    linux下C语言文件操作相关函数
    gcc,gdb用法
  • 原文地址:https://www.cnblogs.com/gispathfinder/p/9029885.html
Copyright © 2011-2022 走看看