2015-06-28 14 views
6

Próbowałem użyć wątku do zarządzania połączeniem z gniazdem. Jednakże pomiędzy wykonaniach moich Proces/gwintów gniazda nie uzyskać oczyszczone i pojawia się wyjątek:PHP jak wymusić uwolnienie gniazda

socket_create_listen(): niezdolne do wiązania się z danym adresem [98] Adres już stosowane w .. Zostanie w końcu wyczyszczony po około 1 minucie.

Szukałem w Internecie, ale nie mogłem znaleźć rozwiązania. Ponadto, jeśli usuniesz kod z pętli while (1) i wykonasz dwukrotnie mój plik php, spowoduje to również ten sam błąd. Spodziewałbym się, że wszystkie ressources zostaną oczyszczone przy wyjściu z procesu.

<?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"; 
    } 
} 
?> 

Wynik:

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 

używam Linux Ubuntu 14.04 i PHP 5.6.8, skompilowany z Pthread.

Odpowiedz

0

Powodem, dla którego nie można ponownie nawiązać połączenia z portem, jest to, że połączenie curl przechodzi do stanu TIME_WAIT po zamknięciu gniazda dla tego połączenia. Jednym ze sposobów uzyskania natychmiastowego zamknięcia połączenia jest dodanie tych dwóch linii przed instrukcją socket_close ($ client) w ramach funkcji processRequest().

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

Próbowałem, ale to nie zadziałało. – Stilgar