zoukankan      html  css  js  c++  java
  • Java网络编程学习A轮_04_TCP连接异常

    参考资料:
    https://huoding.com/2016/01/19/488

    示例代码:
    https://github.com/gordonklg/study,socket module

    A. CLOSE_WAIT

    有时会出现服务器响应极慢、假死的现象,查看 netstat 会发现服务器上存在大量未关闭的 CLOSE_WAIT 状态连接。我们分析下原因。

    首先,CLOSE_WAIT 是被动关闭方才会出现的状态。我们模拟一个场景,客户端建立大量 Socket 连接,同时为每个 Socket 设置超时时间,并且在发生超时时关闭 Socket;服务器端不发送数据也不关闭 Socket。对应测试代码如下:

    gordon.study.socket.basic.wireshark.MockSocketTimeoutThenServerCloseWait_Server.java

    public class MockSocketTimeoutThenServerCloseWait_Server {
     
        @SuppressWarnings("resource")
        public static void main(String[] args) throws Exception {
            Set<Socket> set = new HashSet<>();
            ServerSocket serverSocket = new ServerSocket(8888);
            while (true) {
                Socket socket = serverSocket.accept();
                set.add(socket); // anti gc.
            }
        }
    }
    

    gordon.study.socket.basic.wireshark.MockSocketTimeoutThenServerCloseWait_Client.java

    public class MockSocketTimeoutThenServerCloseWait_Client {
     
        public static void main(String[] args) throws Exception {
            for (int i = 0; i < 100; i++) {
                Thread.sleep(30);
                new Thread(new SocketClient()).start();
            }
        }
     
        private static class SocketClient implements Runnable {
     
            @Override
            public void run() {
                try (Socket socket = new Socket()) {
                    socket.connect(new InetSocketAddress(8888));
                    socket.setSoTimeout(1000);
                    socket.getInputStream().read();
                } catch (Exception e) {
                    System.out.println(e);
                }
            }
        }
    }
    

    100个线程创建了100个 Socket 连接,在1秒钟后读超时,留下了100个 CLOSE_WAIT 状态的连接。

    CLOSE_WAIT 状态的连接并不占用太多操作系统资源,它只是服务器无响应的一种症状,真正的原因还需要自己分析。

    大多数情况下是因为客户端超时直接关闭 Socket,同时服务端没能正确关闭 Socket 导致的。可以通过服务端设置读超时、引入心跳检测等方式修复。

    真正的问题是客户端为什么会超时。考虑是否超时时间设置太短、业务流中是否有耗时(但不太耗资源)的操作、是否允许了请求排队但是队伍太长导致等待中就超时了。

    B. TIME_WAIT

    TIME_WAIT 状态会保持 2 * MSL 时间,这是由 TCP 协议规定的。MSL 是指 TCP 报文段生存的最大时间。

    在高并发场景下,例如 TPS 1k,如果 MSL 为60秒,那么可能会有 120k 个 TIME_WAIT 状态的连接。这会占用大量系统资源。

    TIME_WAIT 一定是主动关闭方才会有的状态,如下图。我们的模拟场景需要由服务方先关闭 Socket。

    tcp

    gordon.study.socket.basic.wireshark.MockServerTimeWait_Server.java

    public class MockServerTimeWait_Server {
     
        @SuppressWarnings("resource")
        public static void main(String[] args) throws Exception {
            ServerSocket serverSocket = new ServerSocket(8888);
            while (true) {
                Socket socket = serverSocket.accept();
                socket.close();
            }
        }
    }
    

    gordon.study.socket.basic.wireshark.MockServerTimeWait_Client.java

    public class MockServerTimeWait_Client {
     
        public static void main(String[] args) throws Exception {
            for (int i = 0; i < 100; i++) {
                Socket socket = new Socket();
                socket.connect(new InetSocketAddress(8888));
                InputStream is = socket.getInputStream();
                while (is.read() != -1) {
                }
                socket.close();
            }
        }
    }
    

    最简单的解决方案就是让客户端作为主动关闭方。被动关闭方是没有 TIME_WAIT 的烦恼的。

  • 相关阅读:
    Android View部分消失效果实现
    Android TV Overscan
    一招搞定短信验证码服务不稳定
    揭秘:网上抽奖系统如何防止刷奖
    SVN迁移到GIT
    Android之高效率截图
    Android TV 开发(5)
    Android 标题栏(2)
    Android 标题栏(1)
    一步步教你学会browserify
  • 原文地址:https://www.cnblogs.com/gordonkong/p/7298233.html
Copyright © 2011-2022 走看看