有没有可能重复使用故意保持打开的套接字?

3
我正在使用PHP sockets扩展(基本上是围绕socket(2)相关的linux系统调用的包装器),并希望在处理一个请求时打开的套接字可以在随后的请求中重复使用。性能是一个关键因素。
我打开的所有套接字都是到同一IP的,这使得像pfsockopen()这样的其他函数的使用变得不可能(因为它每次都会重复使用同一个单独的套接字),而我需要同时使用多个套接字。
问题是:如果我故意保持打开的套接字仍然处于服务状态(我不调用socket_close()socket_shutdown()),并连接具有相同参数的套接字到同一IP以服务下一个请求,那么Linux是否会重复使用之前打开的套接字/文件描述符?
最终我想做的是避免在每个请求上进行TCP握手。
附加信息:
- 我使用Apache工作进程MPM,这意味着不同的请求可以但不一定从不同的进程中提供服务。为了简单起见,让我们假设所有请求都来自同一个进程。 - 我可以获取PHP中已打开和已连接套接字的文件描述符ID。我可以打开、读取和写入/dev/fd/{$id},但是没有任何作用 - 它不与远程服务器通信(也许这是一种天真的方法)。如果有人知道如何使其工作,我会考虑将其视为可以接受的答案。

就我所知,关于低级系统方面的内容很少,我相信Linux系统会尝试每次使用不同的文件描述符而不是回收旧的。例如,在您的情况下,它很可能为您使用的每个套接字分配一个新的文件描述符。此外,如果您不关闭它们,您可能会用完可用的文件描述符。但我想你已经知道了这一点。 - squiguy
内核将为进程重用先前关闭的文件描述符,但不会重用打开的套接字。您有一种方法可以在一个HTTP请求和下一个请求之间保持状态吗? - Nikolai Fetissov
感谢您的评论。@NikolaiNFetissov:是的,我知道,但是PHP不公开原始文件描述符,并且我无法在任何地方保存PHP套接字资源(我假设它们不可序列化)。 - thwd
3个回答

5

如果您希望在同一进程中重用套接字,请保持连接处于打开状态。这实际上是避免TCP握手的唯一选择。请确保开启keepalives:

s.setsockopt( socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

如果您想要生成新进程并将连接传递给它们,是的,它们将能够写入/dev/fd/{$id},并且这将通过网络发送数据。只需确保在exec期间不关闭套接字(了解SOCK_CLOEXEC)。
将套接字传递给不相关的进程是不可能的。您需要使用某种形式的进程间通信来实现这一点,而我不确定TCP握手在局域网或互联网条件下的开销是否足以证明与此相关的复杂性和其他开销。

谢谢您的回答。我相信 SO_KEEPALIVE 只适用于监听套接字,而不是对等套接字。然而,我在问题中也没有明确指出这一点。所以点赞! - thwd
@Tom - 也谢谢你。你可以在客户端使用SO_KEEPALIVE,在连接之前或连接建立后(只是不要在握手过程中)。 - Jirka Hanika

0

虽然Jirka Hanika提供的答案对于大多数系统来说是正确的,但我得出的结论是,遗憾的是它不适用于PHP;使用PHP sockets扩展重复使用套接字在用户空间中是不可能实现的。

导致这个结论的代码是:

function statDescriptors() {
    foreach (glob('/proc/self/fd/*') as $sFile) {
        $r = realpath($sFile);
        // ignore local file descriptors
        if($r === false) {
            echo `stat {$sFile}`, "\n";
        }
    }       
}

header('Content-Type: text/plain');

statDescriptors();

$oSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

socket_set_option($oSocket, SOL_SOCKET, SO_KEEPALIVE, 1);
socket_set_option($oSocket, SOL_SOCKET, SO_REUSEADDR, 1);

socket_connect($oSocket, '173.194.35.33', 80); // Google IP

socket_write($oSocket, 'GET / HTTP/1.0' . "\r\n");
socket_write($oSocket, 'Connection: Keep-Alive' . "\r\n\r\n");

socket_read($oSocket, 1024 * 8);

// socket_close($oSocket); // toggle this line comment during test

echo 'PID is: ', getmypid(), "\n";

statDescriptors();

这段代码将在执行开始和结束时使用stat()函数获取当前进程的打开套接字文件描述符。在此期间,它将使用设置了SO_KEEPALIVE的套接字打开连接,向其写入请求并读取响应。然后,它将选择关闭套接字(切换行注释)并回显当前进程的PID(以确保您在同一进程中)。

您会发现,无论您是否关闭套接字,上一个请求创建的文件描述符都不再存在于本周期的执行开始处,并且将创建并连接一个全新的套接字。

由于该扩展尚未实现SOCK_CLOEXEC,因此我无法测试它。

(此代码已在PHP 5.4.0下进行了测试)


0
如果我故意保留为一个请求打开的套接字(我不调用socket_close()或socket_shutdown()),并连接到相同IP为下一个请求提供服务的具有完全相同参数的套接字;Linux会重复使用先前打开的套接字/文件描述符吗?
不会,但是如果您在同一进程中,则始终可以继续使用原始套接字。您所谈论的实际上是连接池。

你好,感谢你的回答。请记住,PHP是无状态的,没有直接的方法将资源从一个请求周期传递到下一个。 - thwd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接