【问题标题】:PHP how to force freeing socketPHP如何强制释放套接字
【发布时间】:2015-09-15 02:26:41
【问题描述】:

我一直在尝试使用线程来管理套接字连接。但是在我的进程/线程执行之间,套接字没有被清理,我得到了异常:

socket_create_listen(): 无法绑定到给定地址 [98]: 地址 已经在使用... 大约 1 分钟后,它最终会被清理干净。

我在互联网上搜索过,但找不到解决方案。 此外,如果您删除 while(1) 循环中的代码并执行两次我的 php 文件,它也会给出相同的错误。我希望通过进程退出来清理所有资源。

<?php
date_default_timezone_set('UTC');

echo date("H:i:s")."\tINFO: Starting my test\n";
$socket = null;

//start our test
try {
    echo date("H:i:s")."\tINFO: Setup socket for 1st time\n";
    setupSocket();
    usleep(100000);

    //send request to socket via curl
    $command = 'curl -s --no-buffer -d "{\"action\":\"getStatus\"}" -o socket.txt http://localhost:13000 > socket.log 2>&1 &';
    exec($command);
    usleep(100000);

    echo date("H:i:s")."\tINFO: Now processing request\n";
    processRequest();

    echo date("H:i:s")."\tINFO: Close socket\n";
    closeSocket();
} catch (Exception $e) {
    echo date("H:i:s")."\tERROR: ".$e->getMessage() ."\n";
}

while(1) {
    try {        
        echo date("H:i:s")."\tINFO: Try again to set socket...\n";
        setupSocket();
        usleep(100000);

        echo date("H:i:s")."\tINFO: Close socket\n";
        closeSocket();
        break;
    } catch (Exception $e) {
        echo date("H:i:s")."\tERROR: ".$e->getMessage() ."\n";
        sleep(5);
    }
}

function setupSocket($port=13000) {
    global $socket, $logger;

    //Warning: socket_create_listen(): unable to bind to given address [98]: Address already in use in ...
    $socket = @socket_create_listen($port);
    if(!$socket) {
        throw new Exception('Failed to open socket with error \''.socket_strerror(socket_last_error()).'\'');
    }
    if(!socket_set_nonblock ($socket)) {
        throw new Exception('Failed to set the socket as non blocking with error \''.socket_strerror(socket_last_error($socket)).'\'');
    }
    //if(!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
    //    throw new Exception('Failed to set option \''.socket_strerror(socket_last_error($socket)).'\'');
    //}
}

//http://php.net/manual/en/function.socket-close.php
//http://php.net/manual/en/function.socket-shutdown.php
function closeSocket() {
    global $socket, $logger;

    //$arrOpt = array('l_onoff' => 1, 'l_linger' => 0);
    //socket_set_block($socket);
    //socket_set_option($socket, SOL_SOCKET, SO_LINGER, $arrOpt);
    if(!socket_shutdown($socket, 2)) {
        throw new Exception('Failed to shutdown socket with error \''.socket_strerror(socket_last_error($socket)).'\'');
    }
    socket_close($socket);
}

function processRequest() {
    global $socket;
    $client = socket_accept($socket);
    if ($client) {
        echo date("H:i:s")."\tINFO: Got connection\n";
        //we don't care much about the content since this is a test, but still we read :)
        $request = socket_read($client, 1000000);
        $response = array(    
                "head" => array(
                    "HTTP/1.0 200 OK",
                    "Content-Type: application/json"
                ), 
                "body" => json_encode(array('Status'=>true))
            );
        $response['head'][] = sprintf("Content-Length: %d", strlen($response["body"]));
        socket_write($client, implode("\r\n", $response['head']));
        socket_write($client, "\r\n\r\n");
        socket_write($client, $response['body']);
        socket_close($client);

    } else {
        echo date("H:i:s")."\tINFO: Got no connection\n";
    }
}
?>

结果:

    16:05:05    INFO: Starting my test
    16:05:05    INFO: Setup socket for 1st time
    16:05:05    INFO: Now processing request
    16:05:05    INFO: Got connection
    16:05:05    INFO: Close socket
    16:05:05    INFO: Try again to set socket...
    16:05:05    ERROR: Failed to open socket with error 'Address already in use'
    16:05:10    INFO: Try again to set socket...
    16:05:10    ERROR: Failed to open socket with error 'Address already in use'
    16:05:15    INFO: Try again to set socket...
    16:05:15    ERROR: Failed to open socket with error 'Address already in use'
    16:05:20    INFO: Try again to set socket...
    16:05:20    ERROR: Failed to open socket with error 'Address already in use'
    16:05:25    INFO: Try again to set socket...
    16:05:25    ERROR: Failed to open socket with error 'Address already in use'
    16:05:30    INFO: Try again to set socket...
    16:05:30    ERROR: Failed to open socket with error 'Address already in use'
    16:05:35    INFO: Try again to set socket...
    16:05:35    ERROR: Failed to open socket with error 'Address already in use'
    16:05:40    INFO: Try again to set socket...
    16:05:40    ERROR: Failed to open socket with error 'Address already in use'
    16:05:45    INFO: Try again to set socket...
    16:05:45    ERROR: Failed to open socket with error 'Address already in use'
    16:05:50    INFO: Try again to set socket...
    16:05:50    ERROR: Failed to open socket with error 'Address already in use'
    16:05:55    INFO: Try again to set socket...
    16:05:55    ERROR: Failed to open socket with error 'Address already in use'
    16:06:00    INFO: Try again to set socket...
    16:06:00    ERROR: Failed to open socket with error 'Address already in use'
    16:06:05    INFO: Try again to set socket...
    16:06:05    ERROR: Failed to open socket with error 'Address already in use'
    16:06:10    INFO: Try again to set socket...
    16:06:11    INFO: Close socket

我使用 Linux Ubuntu 14.04 和 PHP 5.6.8,用 pthread 编译。

【问题讨论】:

    标签: php sockets


    【解决方案1】:

    您无法重新连接到端口的原因是您的 curl 连接在您关闭该连接的套接字后进入 TIME_WAIT 状态。一种可以立即关闭连接的方法是,在你的 socket_close($client) 语句之前,在你的 processRequest() 函数中添加这两行。

    $linger     = array ('l_linger' => 0, 'l_onoff' => 1);
    socket_set_option($client, SOL_SOCKET, SO_LINGER, $linger);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-04-22
      • 2013-09-04
      • 2015-10-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多