如何程序性地生成连接重置?

9 投票
3 回答
4482 浏览
提问于 2025-04-16 04:28

你一定见过在浏览网页时出现的“连接被重置”的提示信息。(这个提示是来自Firefox,其他浏览器可能会有所不同。)

我需要在需要的时候生成这个提示/错误/状态,以便测试一些解决方法。

那么,我该如何通过编程来生成这个状态呢?(如何从PHP生成一个TCP RST -- 或者其他的网页应用语言?

注意事项和条件:

  1. 不能是一个普遍的IP封锁。测试客户端在不触发这个状态时,仍然需要能够看到测试服务器。

  2. 理想情况下,这个操作应该在网页应用层面完成(比如Python、PHP、ColdFusion、JavaScript等)。直接访问路由器会有问题,访问Apache配置也很麻烦。

  3. 最好是通过获取特定的网页来触发这个状态。

  4. 如果能在标准的商业网页主机上工作,那就更好了。

更新:

仅仅发送RST是不够的,无法造成这个状态。请看我下面的部分回答。

我有一个在本地机器上有效的解决方案,现在需要让它在远程主机上也能工作。

3 个回答

1

我建议通过命令行界面(CLI)使用自定义的套接字来完成这个操作,因为直接修改Apache进程可能会让事情变得复杂。

#!/usr/bin/php -q
<?php

set_time_limit (0);

$sock = socket_create(AF_INET, SOCK_STREAM, 0);

socket_bind($sock, '1.1.1.1', 8081) or die('Could not bind to address');

socket_listen($sock);

$client = socket_accept($sock);
sleep(1);
$pid = getmypid();
exec("kill -9 $pid");
?>

这样做会在Firefox中产生你想要的错误,因为连接在读取之前就已经关闭了。

如果你觉得非常大胆,可以把这个放进一个网页脚本里,但我不建议你这样做,除非你对自己的服务器非常了解,并且知道如何管理它。

1

我觉得你需要比较直接地关闭底层的套接字(socket)。你不能通过JavaScript来做到这一点。对于其他语言,你通常需要获取底层套接字对象的句柄,然后手动调用close()来关闭它。

我也怀疑你能通过Apache来做到这一点,因为是Apache在控制这个套接字,而不是你的应用程序。最好的结果可能只是产生一个HTTP 500错误,这并不是你想要的结果。

2

更新:

这个脚本在测试我们解决连接重置问题时效果不错,所以算是部分答案。

如果有人能找到一个在远程主机上也能完全解决问题的方案,我会很乐意把它标记为答案。


下面这个脚本在同一台机器上运行和测试时每次都能正常工作。但是在远程主机上运行时,浏览器只收到最后三个数据包:

Source     Dest       Protocol  Info
<server>   <client>   TCP       8081 > 1835 [RST] Seq=2 Len=0
<server>   <client>   TCP       8081 > 1835 [RST] Seq=2 Len=0
<server>   <client>   TCP       http > 1834 [ACK] Seq=34 Ack=1 Win=6756 Len=0

如你所见,RST标志被设置并发送了。但是Firefox却没有任何提示,页面是空白的——没有任何信息。

脚本:

<?php
    $time_lim       = 30;
    $listen_port    = 8081;
    echo
       '<h1>Testing generation of a connection reset condition.</h1>
        <p><a target="_blank" href="http://' .$_SERVER["HTTP_HOST"]. ':' .$listen_port. '/">
        Click here to load page that gets reset. You have ' . $time_lim . ' seconds.</a>
        </p>
       '
    ;
    flush ();
?>
<?php
    //-- Warning!  If the script blocks, below, this is not counted against the time limit.
    set_time_limit ($time_lim);

    $socket     = @socket_create_listen ($listen_port);
    if (!$socket) {
        print "Failed to create socket!\n";
        exit;
    }

    socket_set_nonblock ($socket);  //-- Needed, or else script executes until a client interacts with the socket.

    while (true) {
        //-- Use @ to suppress warnings.  Exception handling didn't work.
        $client = @socket_accept ($socket);
        if ($client)
            break;
    }

    /*--- If l_onoff is non-zero and l_linger is zero, all the unsent data will be
        discarded and RST (reset) is sent to the peer in the case of a connection-
        oriented socket.
    */
    $linger     = array ('l_linger' => 0, 'l_onoff' => 1);
    socket_set_option ($socket, SOL_SOCKET, SO_LINGER, $linger);

    //--- If we just close, the Browser gets the RST flag but fails silently (completely blank).
    socket_close ($socket);

    echo "<p>Done.</p>";
?>

撰写回答