无法绑定到地址,因为它已在使用中

时间:2016-06-20 05:34:10

标签: php sockets

我有以下方法:

    public static function listen(int $port, callable $callback) {
        ob_implicit_flush();

        if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
            println("socket_create() failed: reason: " . socket_strerror(socket_last_error()));
        }

        if (socket_bind($sock, '127.0.0.1', $port) === false) {
            println("socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)));
        }

        if (socket_listen($sock, 5) === false) {
            println("socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)));
        }

        if ($callback !== null && is_callable($callback)) {
            call_user_func($callback);
        }

        do {
            if (($msgsock = socket_accept($sock)) === false) {
                println("socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)));
                continue;
            }

            if (false === ($buf = socket_read($msgsock, 2048, PHP_BINARY_READ))) {
                println("socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)));
                continue;
            }

            $eventName = trim(explode(' ', $buf)[1], '/');

            $msg = '';

            foreach (self::$events as $event) {
                if ($event->name() == $eventName) {
                    $msg = $event->headers();
                    $msg .= $event->run();
                    break;
                }
            }

            socket_write($msgsock, $msg, strlen($msg));
            socket_close($msgsock);
            println("socket closed");
        } while (true);

        socket_close($sock);

    }

当我通过命令行运行它时,它工作正常。当我在浏览器中打开页面并从套接字请求内容时,它会输出正确的标题并在页面上显示正确的内容。

编辑代码时,按 Ctrl + C 并重新运行代码,我收到以下错误:

PHP Warning:  socket_bind(): unable to bind address [98]: Address already in use in /path/to/file.php on line 20
socket_bind() failed: reason: Address already in use

这使得更新代码变得困难,因为我必须等待片刻,直到我再次运行代码。按下 Ctrl + C 时,我有办法关闭所有套接字吗?

我注意到只要我不在浏览器中加载它,我就可以开始和停止这个过程很多次。

1 个答案:

答案 0 :(得分:1)

我不懂PHP,但一般来说,你需要为信号SIGINT注册一个处理程序,并关闭该处理程序中的所有开放资源。

请注意,如果您为SIGINT注册处理程序,Ctrl-C将不再终止该进程(它将改为触发处理程序),因此信号处理程序必须确保程序终止,如果这是您想要的

根据此answerHow do I catch a KILL or HUP or User Abort signal?,您应该在PHP中使用pcntl_signal来注册信号

编辑:显然,您必须启用ticks才能触发信号处理程序。一个小例子:

declare(ticks=1);

function sig_handler($signo)
{ 
     switch ($signo) {
         case SIGINT:
             echo "Caught SIGINT, exiting.\n";
             exit;
             break;
         default:
             // handle all other signals
             echo "Caught signal $signo...\n";
     }   

} 

// setup signal handler
pcntl_signal(SIGINT, 'sig_handler');

for ($x = 0; $x <= 50; $x++) {
    echo "main loop: $x\n";
    sleep(1);
}

ticks的值是响应度和开销之间的权衡。