是否可以故意重新使用左开口插座?

时间:2012-06-19 18:54:09

标签: php linux sockets tcp file-descriptor

我正在使用PHP sockets extension(基本上是围绕socket(2)相关的linux系统调用的包装器),并希望重新使用我在后续服务中提供一个请求时打开的套接字。绩效是一个关键因素。

我打开的套接字都是相同的IP,这使得像pfsockopen()这样的其他功能的使用变得不可能(因为它每次都重复使用相同的单个套接字),我一次只需要几个。

问题

如果我离开套接字,我打开服务一个请求故意打开,(我不打电话给socket_close()socket_shutdown())并将具有完全相同参数的套接字连接到下一个服务的同一IP请求; linux会重新使用先前打开的socket / file-descriptor吗?

我最终想做的是避免每次请求都有TCP握手。

其他信息

  • 我使用apache worker MPM - 这意味着不同的请求可以,但不一定是来自不同的进程。为简单起见,我们假设所有请求都来自同一个进程。

  • 我可以在PHP中获取打开并连接的套接字的文件描述符ID。我可以打开并读取和写入/dev/fd/{$id},但没有任何意义 - 它不与远程服务器通信(这可能是一种天真的方法)。如果有人知道如何完成这项工作,我认为这也是一个可以接受的答案。

3 个答案:

答案 0 :(得分:5)

如果要在同一进程中重用套接字,只需将连接保持打开即可。这实际上是避免TCP握手的唯一选择。确保Keepalive已开启:

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

如果您想生成新进程并将连接传递给它们,是的,它们将能够写入/dev/fd/{$id},这将通过网络发送数据。只需确保在exec期间套接字没有关闭(了解SOCK_CLOEXEC)。

无法将套接字传递给不相关的进程。您将不得不使用某种形式的进程间通信来实现这一点,而且我不确定在Intranet或Internet条件下TCP握手的开销是否足以证明与此相关的复杂性和其他开销。

答案 1 :(得分:0)

  

如果我离开插座,我打开服务一个请求故意打开,   (我不调用socket_close()或socket_shutdown())并连接一个   套接字具有与下一个服务相同IP的完全相同的参数   请求;将linux重新使用以前打开的套接字/   文件描述符?

不,但如果你在同一个过程中,你可以随时继续使用原始的。你在谈论的是真正的连接池。

答案 2 :(得分:0)

虽然Jirka Hanika给出的答案对大多数系统都是正确的,但我得出的结论是遗憾的是它不适用于PHP;使用PHP sockets extension重新使用套接字是不可能从用户空间实现的。

导致这一结论的代码是:

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测试的)