You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SSL Socket连接偶发创建失败咨询:执行停滞无报错排查

Socket连接偶发停滞无响应,REST调用却正常

问题背景与代码实现

我通过继承Thread的类尝试与指定地址建立Socket连接,重写的run方法及相关代码如下:

private String headerForGetConnection() {
    return String.format(
            "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nx-sessioncookie: %s\r\nAccept: application/x-rtsp-tunnelled\r\nPragma: no-cache\r\nCache-Control: no-cache\r\n\r\n", 
            addressAttributes(), this.USER_AGENT, this.session_cookie
    );
}

private String addressAttributes() {
    return "/img/video.sav?MAC=" + this.camera.getDevice().getMac() + "&GUID=" + "GUID value";
}

@Override
public void run() {
    super.run();

    int count = 0;

    int MAX_CONNECTION_TRY = 3;
    while (count < MAX_CONNECTION_TRY && !shouldCloseConnection) {

        TCLog.d(TAG, "counting value " + count);
        count++;
        socket = null;
        bufferedInputStream = null;
        printWriter = null;

        try {
            TCLog.d(TAG, "media serverAddress " + serverAddress + " serverPort == " + serverPort);

            SocketFactory socketFactory = SSLSocketFactory.getDefault();
            socket = socketFactory.createSocket(serverAddress, serverPort);
            TCLog.d(TAG, "Media server socket value after connecting to address and port == " + socket);
            if (null == bufferedInputStream) {
                bufferedInputStream = new BufferedInputStream(socket.getInputStream());
            }

        } catch(UnknownHostException e) {
            TCLog.d(TAG, "value UnknownHostException" + e.getMessage());
            handleError(e.getMessage());
        } catch(IOException e) {
            TCLog.d(TAG, "value IOException " + e.getMessage());
            handleError(e.getMessage());
        }

        try {
            printWriter = new PrintWriter(socket.getOutputStream());
            printWriter.println(serverHeader);
            printWriter.flush();
            isFirstMessage = true;
        } catch(IOException e) {
            TCLog.d(TAG, "value IOException while writing " + e.getMessage());
            handleError(e.getMessage());
        }

        while (!shouldCloseConnection) {
            try {
                String dataToSend = null;
                byte[] readingData = new byte[BUFFER_SIZE];
                int read;
                byte[] data = null;
                if ((read = bufferedInputStream.read(readingData)) != -1) {
                    TCLog.d(TAG, "OTHER read value " + read);
                    data = Arrays.copyOfRange(readingData, 0, read);
                    dataToSend = new String(data);
                }
                if (null != dataToSend) {
                    TCLog.d(TAG, "message from server " + dataToSend);
                    if (isFirstMessage) {

                        if (!dataToSend.contains("200 OK")) {

                            if (dataToSend.contains("303")) {

                                TCLog.d(TAG, "when there is redirect");
                                TCLog.d(TAG, "splitting string value");

                                String[] videos = dataToSend.split("https://");

                                TCLog.d(TAG, "values 1 " + videos[0]);
                                TCLog.d(TAG, "value 2 " + videos[1]);

                                String redirectUrl = videos[1];

                                videos = redirectUrl.split(":");

                                redirectUrl = videos[0];

                                TCLog.d(TAG, "redirect URL is " + redirectUrl);

                                isRedirectUrl = true;

                                this.serverAddress = redirectUrl;
                                Reflector.address = this.serverAddress;
                                break;
                            } else if (dataToSend.contains("HTTP/1.0 4")) {
                                isRedirectUrl = false;
                                break;
                            }
                        } else {
                            this.isFirstMessage = false;
                            if (null != listener) {
                                this.listener.getConnectionConnectedSuccessfully();
                            }
                        }
                    }
                    else {
                        if (null != listener) {
                            listener.getConnectionRawData(data, dataToSend.trim());
                        }
                    }
                }
            } catch(IOException e) {
                TCLog.d(TAG, "value IOException while reading " + e.getMessage());
                handleError(e.getMessage());
            } finally {
                TCLog.d(TAG, "inside finally block");
                if (isFirstMessage && !isRedirectUrl) {
                    handleError("Unable to play video");

                    if (null != printWriter) {
                        printWriter.close();
                    }
                    try {
                        TCLog.d(TAG, "inside finally try block. Closing connections");
                        if (null != bufferedInputStream) {
                            bufferedInputStream.close();
                        }
                        if (null != socket && socket.isConnected()) {
                            socket.close();
                        }
                    } catch(IOException e) {
                        TCLog.d(TAG, "error while closing streams " + e.getMessage());
                    }
                }
            }
        }
        if (!isRedirectUrl) {
            break;
        }
    }
    if (count >= MAX_CONNECTION_TRY && !shouldCloseConnection) {
        handleError("Unable to establish connection");
    }
}

问题现象

程序偶发出现异常:打印media serverAddress日志后,Socket创建后的代码完全停滞执行,无任何报错日志,但有时连接又能成功完成。

补充信息

Socket连接成功后需发送特定GET请求,我将该请求转为API/REST调用时,每次都能收到服务器响应(即使有10-12秒延迟),怀疑并非服务器配置问题。


排查思路与解决建议

1. 给Socket设置超时时间

当前代码创建Socket时未设置连接超时和读取超时,一旦服务器端响应缓慢或网络异常,createSocket或后续的read操作会无限阻塞。添加超时设置:

socket = socketFactory.createSocket(serverAddress, serverPort);
socket.setSoTimeout(15000); // 设置15秒读取超时
// 或者创建时直接指定连接超时
socket = socketFactory.createSocket(serverAddress, serverPort, InetAddress.getLocalHost(), 0, 15000);

超时后会抛出SocketTimeoutException,可捕获处理避免线程永久挂起。

2. 修复异常处理漏洞

  • 如果createSocket抛出异常,后续printWriter初始化逻辑仍会执行,此时socket为null会触发未捕获的NPE,需将printWriter初始化移到createSocket成功后的try块内。
  • 线程终止标志shouldCloseConnection需声明为volatile,确保线程能及时感知终止信号,避免可见性问题导致的无效阻塞。

3. 排查SSL握手阻塞

使用SSLSocket时,握手阶段可能因证书验证、加密套件协商等问题阻塞。可开启SSL调试日志定位问题:

System.setProperty("javax.net.debug", "ssl");

同时可给SSL握手添加超时控制,避免长时间阻塞。

4. 检查BufferedInputStream阻塞逻辑

bufferedInputStream.read(readingData)是阻塞式操作,读取前可先检查socket.isClosed()状态,结合超时设置,避免在已关闭的Socket上持续等待。

5. 验证线程状态与资源泄漏

停滞时用jstack工具查看线程状态,确认是WAITING还是BLOCKED状态,定位具体阻塞点。同时检查是否存在Socket资源未正确关闭导致的连接池耗尽问题。

6. 改用成熟HTTP客户端替代手动Socket实现

REST调用使用的成熟HTTP客户端(如OkHttp)会自动处理超时、重定向、连接池等逻辑,可直接改用这类库替代手动Socket实现,减少底层问题排查成本。


内容的提问来源于stack exchange,提问作者Arun Mehra

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 05:08:09