【问题标题】:Is there a way to simulate Socket and Connection timeout?有没有办法模拟 Socket 和 Connection 超时?
【发布时间】:2026-01-30 18:20:04
【问题描述】:

我有一段代码使用 HTTP 连接与第三方集成,它以不同的方式处理套接字超时和连接超时。

我一直在尝试模拟和测试第三方可能出现的所有场景。能够通过连接到被服务器防火墙阻止的端口来测试连接超时,例如81 端口。

但是我无法模拟套接字超时。如果我的理解没有错,套接字超时与连续的数据包流相关,并且连接断开。有什么方法可以模拟吗?

【问题讨论】:

    标签: java http httpurlconnection socket-timeout-exception


    【解决方案1】:

    所以我们这里说的超时有几种,一种是连接到服务器(连接超时),另一种是在一段时间内没有通过套接字发送或接收数据时发生超时(空闲超时)。

    节点套接字有一个套接字超时,可用于综合连接和空闲超时。这可以通过将套接字超时设置为连接超时,然后在连接时将其设置为空闲超时来完成。

    示例:

            const request = http.request(url, {
                timeout: connectTimeout,
            });
            request.setTimeout(idleTimeout);
    
    

    之所以有效,是因为选项中的超时时间在创建套接字时立即设置,setTimeout 函数在连接时在套接字上运行!

    无论如何,问题是关于如何测试连接超时。好的,让我们先停止空闲超时。我们可以通过一段时间不发送任何数据来简单地测试一下,这会导致超时。检查!

    连接超时有点难以测试,首先想到的是我们需要一个连接的地方,它不会出错,但也不能连接。这会导致超时。但是我们到底要如何在节点中模拟呢?

    如果我们跳出框框思考一下,那么我们可能会发现这个超时时间大约是连接所需的时间。为什么连接需要这么长时间并不重要。我们只需要延迟连接所需的时间。这不一定是服务器的事情,我们也可以在客户端上做。毕竟这是连接的部分,如果我们可以在那里延迟它,我们可以测试超时。

    那么我们怎么能延迟客户端的连接呢?好吧,我们可以使用 DNS 查找。在建立连接之前,会完成 DNS 查找。如果我们只是将其延迟 5 秒左右,我们可以非常轻松地测试连接超时。

    这就是代码的样子:

    import * as dns from "dns";
    import * as http from "http";
    
    const url = new URL("http://localhost:8080");
    
    const request = http.request(url, {
        timeout: 3 * 1000, // connect timeout
        lookup(hostname, options, callback) {
            setTimeout(
                () => dns.lookup(hostname, options, callback),
                5 * 1000,
            );
        },
    });
    request.setTimeout(10 * 1000); // idle timeout
    
    request.addListener("timeout", () => {
        const message = !request.socket || request.socket.connecting ?
            `connect timeout while connecting to ${url.href}` :
            `idle timeout while connected to ${url.href}`;
    
        request.destroy(new Error(message));
    });
    
    

    在我的项目中,我通常使用我注入的代理。然后代理进行延迟查找。像这样:

    import * as dns from "dns";
    import * as http from "http";
    
    const url = new URL("http://localhost:8080");
    
    const agent = new http.Agent({
        lookup(hostname, options, callback) {
            setTimeout(
                () => dns.lookup(hostname, options, callback),
                5 * 1000,
            );
        },
    });
    
    const request = http.request(url, {
        timeout: 3 * 1000, // connect timeout
        agent,
    });
    request.setTimeout(10 * 1000); // idle timeout
    
    request.addListener("timeout", () => {
        const message = !request.socket || request.socket.connecting ?
            `connect timeout while connecting to ${url.href}` :
            `idle timeout while connected to ${url.href}`;
    
        request.destroy(new Error(message));
    });
    

    编码愉快!

    【讨论】:

    • 哇哈哈我才意识到这个问题是关于Java的!我提供的解决方案是在 node.js 中,但我想它也可以在 Java 中完成 :-)
    【解决方案2】:

    “连接超时”决定了建立 TCP 连接可能需要多长时间,这一切都发生在任何与 HTTP 相关的数据通过线路发送之前。通过连接到阻塞的端口,您只测试了部分连接超时,因为没有建立连接。通常,本地网络上的 TCP 连接创建(建立)非常快。但是当连接到世界另一端的服务器时,建立 TCP 连接可能需要几秒钟。

    “套接字超时”是一个有点误导性的名称——它只是确定您(客户端)将等待来自服务器的答案(数据)的时间。换句话说,Socket.read() 函数在等待数据时会阻塞多长时间。

    正确测试这些功能涉及创建服务器套接字或 (HTTP) Web 服务器,您可以将其修改为非常慢。描述如何创建和使用服务器套接字进行连接超时测试(如果可能的话)在这里回答太多了,但套接字超时测试是一个常见问题 - 参见例如here(我刚刚搜索了“模拟网络服务器测试超时”),这会导致像 MockWebServer 这样的工具。 “MockWebServer”可能也有测试连接超时的选项(我没有使用“MockWebServer”),但如果没有,其他工具可能有。

    最后一点:您最好在超时设置方面测试您对第三方 HTTP 库的使用,即使这需要一些努力。可能发生的最糟糕的情况是库以某种方式未使用代码中的套接字超时设置,并且使用了“永远等待”的默认套接字超时。这可能会导致您的应用程序无缘无故地什么都不做(“挂起”)。

    【讨论】:

    • '通过连接到一个被阻塞的端口,你只测试了部分连接超时,因为没有建立连接':false。他已经完全测试过了。