As we all know, when we use PHP to develop Web programs, we need to use a Web server such as Apache or Nginx to support, so there is no way to directly use PHP to develop HTTP server, the answer is yes, of course, recently read the source code of Workerman framework, So their copy wrote a simple HTTP server, learning based, interested friends can see.

preface

This application is mainly around Socket programming, HTTP protocol, events, process control and signal knowledge points to achieve, the general idea is the main process to create A TCP server, create a child process to listen to events to accept requests and process business and achieve HTTP protocol, at the same time the main process registered signals and monitor signals and process signal events. Monitoring child process status and controlling child process exit, restart, etc., I will go over the basic knowledge.

TCP server

HTTP protocol is an application based on TCP protocol, so the implementation of HTTP server must first implement TCP server, PHP can use Sockets related functions to provide a TCP server, in addition, it is recommended to use more convenient Streams related functions to implement TCP server. Here are two ways to implement it:

Sockets related functions:

<? The PHP $address = '127.0.0.1; $port = 1234; $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); // AF_INET=ipv4, AF_INET6=ipv6, SOCK_STREAM= TCP, SOCK_DGRAM=udp socket_set_block($sock); Socket_bind ($sock, $address, $port); // Bind to socket port socket_listen($sock, 4); While (1) {$newSocket = socket_accept($sock); If ($newSocket) {while (($buffer = socket_read($newSocket,1024))! == false ) { echo "get client data:{$buffer}\n"; $msg = "server say hello world\n"; socket_write($newSocket, $msg); } socket_close($newSocket); } else { echo "no newSocket\n"; }}Copy the code

Streams related functions:

<? php $errno = 0; $errmsg = ''; $socket = stream_socket_server(' TCP :// 0.0.0.00:1234 ', $errno, $errmsg); Stream_set_blocking ($socket, 0); While (1) {$newSocket = stream_socket_accept($socket); If ($newSocket) {while ($buffer = fread($newSocket, 1024)! Echo "get client data:{$buffer}\n"; $msg = "server say hello world\n"; fwrite($newSocket, $msg); } fclose($newSocket); } else { echo "no newSocket\n"; }}Copy the code

Sockets related functions provide a much lower level interface, and in our case, using Streams related functions is much more concise and generic.

The HTTP server

HTTP is an application based on TCP. To implement the HTTP server, you only need to implement THE HTTP protocol on the TCP server. The following is the DECODING and encoding of THE HTTP protocol that only supports GET requests:

HTTP decoding:

<? PHP /** * HTTP decode (GET request) * @param $content * @return array */ public function httpDecode($content) $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array(); $_SERVER = array( 'QUERY_STRING' => '', 'REQUEST_METHOD' => '', 'REQUEST_URI' => '', 'SERVER_PROTOCOL' => '', 'SERVER_NAME' => '', 'HTTP_HOST' => '', 'HTTP_USER_AGENT' => '', 'HTTP_ACCEPT' => '', 'HTTP_ACCEPT_LANGUAGE' => '', 'HTTP_ACCEPT_ENCODING' => '', 'HTTP_COOKIE' => '', 'HTTP_CONNECTION' => '', 'REMOTE_ADDR' => '', 'REMOTE_PORT' => '0', 'REQUEST_TIME' => time() ); List ($http_header, $http_body) = explode("\r\n\r\n", $content, 2); $header_data = explode("\r\n", $http_header); list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ', $header_data[0]); unset($header_data[0]); foreach ($header_data as $content) { // \r\n\r\n if (empty($content)) { continue; } list($key, $value) = explode(':', $content, 2); $key = str_replace('-', '_', strtoupper($key)); $value = trim($value); $_SERVER['HTTP_' . $key] = $value; $_SERVER['QUERY_STRING'] = parse_URL ($_SERVER['REQUEST_URI'], PHP_URL_QUERY); $_SERVER['QUERY_STRING'] = parse_URL ($_SERVER['REQUEST_URI'], PHP_URL_QUERY); if ($_SERVER['QUERY_STRING']) { // $GET parse_str($_SERVER['QUERY_STRING'], $_GET); } else { $_SERVER['QUERY_STRING'] = ''; } // REQUEST $_REQUEST = array_merge($_GET, $_POST); return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES); }Copy the code

HTTP code:

<? Public function httpEncode($content) {$header = httpEncode($content) "HTTP / 1.1 200 OK \ r \ n"; $header .= "Content-Type: text/html; charset=utf-8\r\n"; $header .= "Connection: keep-alive\r\n"; $header. = "Server: workerman / 3.5.4 \ r \ n"; $header .= "Content-Length: " . strlen($content) . "\r\n\r\n"; return $header . $content; }Copy the code

The data received by TCP server is decoded in accordance with the HTTP protocol, and the decoded data is registered into THE PHP global variable. PHP obtains the request data from the global variable, and returns the output content according to the HTTP protocol encoding after business processing, that is, an HTTP server is realized. The complete HTTP protocol encoding and decoding can be refer to Workman source code.

The event

The disadvantage of the above HTTP server is that it can only process one request at a time, but through event processing, we can accept multiple requests at the same time, and process the business when the request is readable, so as to achieve IO reuse. PHP can use Libevent to use advanced event processing mechanisms such as Epoll and Kqueue. Related functions and applications are described as follows:

Correlation function:

  • Create and initialize a new event library:
resource event_base_new ( void )
Copy the code
  • Create a new event:
resource event_new ( void )
Copy the code
  • Prepare an event:
bool event_set ( resource $event , mixed $fd , int $events , mixed $callback [, mixed $arg ] )
Copy the code

Fd: signal or stream resource.

Events: indicates events. The following flags: EV_TIMEOUT, EV_signal, EV_read, EV_write, and EV_PERSIST.

Callback: processing function.

Arg: Handles function arguments

  • Associate event libraries with events:
bool event_base_set ( resource $event , resource $event_base )
Copy the code
  • Add events to the monitoring event set:
bool event_add ( resource $event [, int $timeout = -1 ] )
Copy the code
  • Start the event loop:
int event_base_loop ( resource $event_base [, int $flags = 0 ] )
Copy the code

Examples of official listening standard input events:

<? php function print_line($fd, $events, $arg) { static $max_requests = 0; $max_requests++; if ($max_requests == 10) { // exit loop after 10 writes event_base_loopexit($arg[1]); } // print the line echo fgets($fd); } // create base and event $base = event_base_new(); $event = event_new(); $fd = STDIN; // set event flags event_set($event, $fd, EV_READ | EV_PERSIST, "print_line", array($event, $base)); // set event base event_base_set($event, $base); // enable event event_add($event); // start event loop event_base_loop($base);Copy the code

Event-based TCP server:

<? $errno = 0; $errmsg = ''; $socket = stream_socket_server(' TCP :// 0.0.0.00:1234 ', $errno, $errmsg); stream_set_blocking($socket, 0); $base = event_base_new(); $base = event_base_new(); $event = event_new(); event_set($event, $socket, EV_READ | EV_PERSIST, "acceptConnect", array($event, $base)); event_base_set($event, $base); event_add($event); event_base_loop($base); // Start event polling, Function acceptConnect($socket, $events, $arg) { $newSocket = @stream_socket_accept($socket, 0, $remote_address); // Set the second parameter to 0, no blocking, no warning if (! $newSocket) { return; } stream_set_blocking($newSocket, 0); Echo "accept new connection \n"; $event = event_new(); // Listen for newSocket file descriptors, listen for events EV_READ: readable, EV_PERSIST: persistent (disconnection events closed and listened); event_set($event, $newSocket, EV_READ | EV_PERSIST, "acceptData", array($event, $arg[1])); event_base_set($event, $arg[1]); event_add($event); } function acceptData($newSocket, $events, $arg) { $buffer = @fread($newSocket,1024); / / get the data if ($buffer = = = '| | $buffer = = = false) {if (feof ($newSocket) | |! Is_resource ($newSocket) | | $buffer = = = false) {echo "connection quit \ n"; event_del($arg[0]); // Close the connection @fclose($newSocket); // Close the connection. } } echo "get client data:{$buffer}\n"; $msg = "server say hello world\n"; fwrite($newSocket, $msg); }Copy the code

The event-based TCP server and HTTP protocol decoder realize an event-based HTTP server.

Process control and signalling

Event-based HTTP server can process multiple requests at the same time, but it is still a single process mode, which cannot play a large performance. Through process control and signal mechanism, we can customize our HTTP server. PHP can realize process control and use signal to communicate through PCNTL function and POSIX function. The functions and uses involved in creating new processes and implementing signal mechanisms are described as follows:

Create child process and listen for child process exit status related functions:

  • Return the current process ID:
int posix_getpid ( void )
Copy the code

Returns the process ID number of type integer.

  • Create a child process:
int pcntl_fork ( void )
Copy the code

The parent and child processes both start at fork and continue. The parent process returns the fork number, while the child process returns 0.

  • Child process status waiting or returning to fork:
int pcntl_wait ( int &$status [, int $options = 0 ] )
Copy the code

The wait function scrapes the execution of the current process until a child process exits or receives a signal to interrupt the current process or call a signal handler.

Create a child process and listen for its exit status using these three functions:

<? php $pid = pcntl_fork(); If ($pid == -1) {die($pid == -1); } else if ($pid == 0) {echo "this is a child, pid:".posix_getpid (). "\n"; sleep(5); exit(0); } else {// Run echo "This is the parent, pid:".posix_getpid (). "\n"; $status = 0; $pid = pcntl_wait($status, WUNTRACED); Echo "exit, pid:". $pid. "\n"; echo "exit, pid:". $pid. }Copy the code

Next, signal processing related functions are introduced:

  • Install a signal handler:
bool pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] )
Copy the code

Signo: indicates the signal number. For example, if a SIGINT signal is sent when CTRL+C is pressed, run the kill -l command to view all signals.

Handler: Signal handler, user function or method, or system constant SIG_IGN or SIG_DFL (default signal handler).

Restart_syscalls: Specifies whether the system call restart is available when the signal arrives.

  • The handler that calls the waiting signal:
bool pcntl_signal_dispatch ( void )
Copy the code

The function pcnTL_signal_dispatch () calls each processor that waits for signals to pass through pCNTL_signal ().

  • To send a signal to the specified process:
bool posix_kill ( int $pid , int $sig )
Copy the code

Pid: indicates the process ID

Sig: indicates signals. Run the kill -l command to view all signals

Register, distribute, and receive signals using the above functions:

<? PHP function signalHandler($sigNo) {switch ($sigNo) {// stop. case SIGINT: echo "process exit \n"; exit(0); // user1 case SIGUSR1: echo "handle user-defined signal \n"; break; $pid = posix_getgid(); $pid = posix_getgid(); echo "pid:{$pid}\n"; // Register SIGINT signal, CTRL + C, or kill SIGINT $PID, or use posix_kill to trigger pCNTL_signal (SIGINT, 'SignalHandler', false); // Register SIGUSR1, pCNTL_signal (SIGUSR1, 'SignalHandler', false); posix_kill($pid, SIGUSR1); while(1) { sleep(1); pcntl_signal_dispatch(); // Signal distribution}Copy the code

With process control and signaling mechanisms, we can create child processes to handle HTTP requests, and the main process registers and processes signals and monitors and controls child processes.

PHP developed HTTP server

Putting the above points together, we can implement the final HTTP server:

HTTP server developed in PHP:

<?php

// 解析命令
$command  = isset($argv[1]) ? trim($argv[1]) : '';
$available_commands = array(
    'start',
    'stop',
    'reload',
    'status',
);
$usage = "Usage: php index.php {" . implode('|', $available_commands) . "}\n";
if (empty($command) || !in_array($command, $available_commands)) {
    exit($usage);
}

$worker = new Worker();

switch ($command) {
    case 'start':
        $worker->start();
        break;
    case 'stop':
    case 'reload':
    case 'status':
        $worker->sendSignalToMaster($command);
        break;
}


class Worker
{

    public $count = 4; //子进程数
    public $localSocket = 'tcp://0.0.0.0:2345'; // 监听地址
    public $onMessage = null; // 处理函数
    
    private $masterPidFile = 'masterPidFile.pid'; // 主进程pid
    private $masterStatusFile = 'masterStatusFile.status'; // 主进程状态文件
    private $forkArr = array(); // 子进程pid数组
    private $socket = null; // 监听socket
    private $newSocket = null; // 连接socket
    private $masterStop = 0; // 主进程是否停止
    private $connectionCount = 0; //每个子进程到连接数
    private $requestNum = 0; //每个子进程总请求数

    public function __construct()
    {
        if (!$this->onMessage) { // 默认处理
            $this->onMessage = function($connection)
            {
                //var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES);
                // 发送数据给客户端
                $connection->sendData("hello world \n");
            };
        }
    }

    /**
     * 主进程启动
     */
    public function start()
    {
        // 判断当前程序是否已经启动
        $masterPidFileExist = is_file($this->masterPidFile);
        if ($masterPidFileExist) {
            exit("当前程序已经在运行,请不要重启启动\n");
        }

        // 保存主进程pid到文件用于stop,reload,status等命令操作
        $masterPid = posix_getpid();
        file_put_contents($this->masterPidFile, $masterPid);

        // 注册主进程信号,pcntl_signal第三个参数设置成false,才会有信号时被pcntl_wait调用
        pcntl_signal(SIGINT, array($this, 'masterSignalHandler'), false); // 退出,用于stop命令或主进程窗口按下ctrl+c
        pcntl_signal(SIGUSR1, array($this, 'masterSignalHandler'), false); // 自定义信号1,用于reload命令
        pcntl_signal(SIGUSR2, array($this, 'masterSignalHandler'), false); // 自定义信号2,用户status命令

        // 主进程创建tcp服务器
        $errno = 0;
        $errmsg = '';
        $socket = stream_socket_server($this->localSocket, $errno, $errmsg);

        // 尝试打开KeepAlive TCP和禁用Nagle算法。
        if (function_exists('socket_import_stream')) {
            $socketImport = socket_import_stream($socket);
            @socket_set_option($socketImport, SOL_SOCKET, SO_KEEPALIVE, 1);
            @socket_set_option($socketImport, SOL_TCP, TCP_NODELAY, 1);
        }

        // Non blocking.
        stream_set_blocking($socket, 0);
        $this->socket = $socket;

        // 创建count个子进程,用于接受请求和处理数据
        while(count($this->forkArr) < $this->count) {
            $this->fork();
        }

        // 主进程接受信号和监听子进程信号
        while(1) {

            //sleep(1);
            pcntl_signal_dispatch(); // 信号分发
            $status = 0;
            $pid = pcntl_wait($status, WUNTRACED); // 堵塞直至获取子进程退出或中断信号或调用一个信号处理器,或者没有子进程时返回错误
            pcntl_signal_dispatch();
            if ($pid > 0) {
                // 子进程退出
                echo "子进程退出pid:{$pid}\n";
                unset($this->forkArr[$pid]);
                // 关闭还是重启
                if (!$this->masterStop) {
                    // 重启
                    $this->fork();
                }
            } else {
                // 主进程退出状态并且没有子进程时退出
                if ($this->masterStop && empty($this->forkArr)) {
                    unlink($this->masterPidFile);
                    fclose($this->socket);
                    echo "主进程退出\n";
                    exit(0);
                }
            }
        }
    }

    /**
     * 主进程处理信号
     * @param $sigNo
     */
    public function masterSignalHandler($sigNo)
    {
        switch ($sigNo) {
            // Stop.
            case SIGINT:
                // 退出,先发送子进程信号关闭子进程,再等待主进程退出
                foreach ($this->forkArr as $pid) {
                    echo "关闭子进程pid:{$pid}\n" ;
                    posix_kill($pid, SIGKILL);
                }
                $this->masterStop = 1; // 将主进程状态置成退出
                break;
            // user1
            case SIGUSR1:
                // 重启,关闭当前存在但子进程,主进程会监视退出的子进程并重启一个新子进程
                foreach ($this->forkArr as $pid) {
                    echo "关闭子进程pid:{$pid}\n" ;
                    posix_kill($pid, SIGKILL);
                }
                break;
            // user2
            case SIGUSR2:
                echo "将状态信息保存至文件:{$this->masterStatusFile}\n";
                // 将状态信息保存至文件
                $str = "---------------------STATUS---------------------\n";
                $str .= 'PHP version:' . PHP_VERSION . "\n";
                $str .= 'processes num:' . $this->count . "\n";
                $str .= "---------------------PROCESS STATUS---------------------\n";
                $str .= "pid\n";

                foreach ($this->forkArr as $childPid) {
                    $str .= $childPid."\n";
                }
                file_put_contents($this->masterStatusFile, $str);
                break;
            default:
                // 处理所有其他信号
        }
    }

    /**
     * 创建子进程
     */
    public function fork()
    {
        $pid = pcntl_fork();
        if ($pid == -1) {
            die('子进程创建失败');
        } else if ($pid == 0) {
            // 以下代码在子进程中运行

            // 创建event事件
            $base = event_base_new();
            $event = event_new();

            // 设置event监听事件,监听socket文件描述符,监听事件EV_READ:可读,EV_PERSIST:持续化(断开连接可被监听到)
            event_set($event, $this->socket, EV_READ | EV_PERSIST, array($this, "acceptConnect"), array($event, $base));
            // 设置event
            event_base_set($event, $base);
            event_add($event);
            // 开始event轮询,当socket文件描述符有可读或断开会触发acceptConnect函数处理
            event_base_loop($base);
        } else {
            // 主进程将子进程pid保存到数组
            echo "创建子进程pid:{$pid}\n";
            $this->forkArr[$pid] = $pid;
        }
    }

    /**
     * 子进程接受请求
     * @param $socket
     * @param $events
     * @param $arg
     */
    public function acceptConnect($socket, $events, $arg)
    {
        $newSocket = @stream_socket_accept($socket, 0, $remote_address); // 第二个参数设置0,不堵塞,未获取到会警告
        //有一个连接过来时,子进程都会触发本函数,但只有一个子进程获取到连接并处理
        if (!$newSocket) {
            return;
        }

        echo "acceptConnect\n";
        $this->connectionCount++;

        stream_set_blocking($newSocket, 0);
        // 兼容hhvm
        if (function_exists('stream_set_read_buffer')) {
            stream_set_read_buffer($newSocket, 0);
        }

        // 子进程添加一个事件在newSocket文件描述符上
        $event = event_new();
        // 设置event监听事件,监听newSocket文件描述符,事件为EV_READ:可读,EV_PERSIST:持续化(断开连接可被监听到)
        event_set($event, $newSocket, EV_READ | EV_PERSIST, array($this, "acceptData"), array($event, $arg[1]));
        event_base_set($event, $arg[1]);
        event_add($event);
    }

    /**
     * 子进程处理数据
     * @param $newSocket
     * @param $events
     * @param $arg
     */
    public function acceptData($newSocket, $events, $arg)
    {
        $this->newSocket = $newSocket;
        // http服务器(HTTP1.1默认使用keep-alive保持连接)
        $buffer = @fread($newSocket,65535); //获取数据
        //echo "获取客户端数据:{$buffer}\n";
        if ($buffer === '' || $buffer === false) {
            if (feof($newSocket) || !is_resource($newSocket) || $buffer === false) {
                echo "客户端关闭\n";
                event_del($arg[0]); //关闭连接事件
                @fclose($this->newSocket); // 关闭连接
                $this->connectionCount--;
                return;
            }
        }
        $this->requestNum++;
        $this->httpDecode($buffer); // http解码
        call_user_func($this->onMessage, $this); // 调用处理函数

        /*// tcp服务器
        $buffer = fread($newSocket,1024);
        if ($buffer === '' || $buffer === false) {
            if (feof($newSocket) || $buffer === false) {
                echo "客户端关闭连接\n";
                event_del($arg[0]);
                fclose($newSocket);
            }
        } else {
            echo "获取客户端数据:" . $buffer;
            $msg = "hello client\n";
            fwrite($newSocket, $msg);
        }*/
    }

    /**
     * http服务器返回数据
     * @param $sendBuffer
     * @return bool
     */
    public function sendData($sendBuffer) {
        $msg = $this->httpEncode($sendBuffer); // http编码
        fwrite($this->newSocket, $msg, 8192);
        return true;
    }

    /**
     * http解码(仅GET请求)
     * @param $content
     * @return array
     */
    public function httpDecode($content)
    {
        // 全局变量
        $_POST = $_GET = $_COOKIE = $_REQUEST = $_SESSION = $_FILES = array();

        $_SERVER = array(
            'QUERY_STRING'         => '',
            'REQUEST_METHOD'       => '',
            'REQUEST_URI'          => '',
            'SERVER_PROTOCOL'      => '',
            'SERVER_NAME'          => '',
            'HTTP_HOST'            => '',
            'HTTP_USER_AGENT'      => '',
            'HTTP_ACCEPT'          => '',
            'HTTP_ACCEPT_LANGUAGE' => '',
            'HTTP_ACCEPT_ENCODING' => '',
            'HTTP_COOKIE'          => '',
            'HTTP_CONNECTION'      => '',
            'REMOTE_ADDR'          => '',
            'REMOTE_PORT'          => '0',
            'REQUEST_TIME'         => time()
        );

        // 解析头部
        list($http_header, $http_body) = explode("\r\n\r\n", $content, 2);
        $header_data = explode("\r\n", $http_header);

        list($_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'], $_SERVER['SERVER_PROTOCOL']) = explode(' ',
            $header_data[0]);
        unset($header_data[0]);
        foreach ($header_data as $content) {
            // \r\n\r\n
            if (empty($content)) {
                continue;
            }
            list($key, $value)       = explode(':', $content, 2);
            $key                     = str_replace('-', '_', strtoupper($key));
            $value                   = trim($value);
            $_SERVER['HTTP_' . $key] = $value;
        }

        // 查询字符串
        $_SERVER['QUERY_STRING'] = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY);
        if ($_SERVER['QUERY_STRING']) {
            // $GET
            parse_str($_SERVER['QUERY_STRING'], $_GET);
        } else {
            $_SERVER['QUERY_STRING'] = '';
        }

        // REQUEST
        $_REQUEST = array_merge($_GET, $_POST);

        return array('get' => $_GET, 'post' => $_POST, 'cookie' => $_COOKIE, 'server' => $_SERVER, 'files' => $_FILES);
    }

    /**
     * http编码(仅GET请求)
     * @param $content
     * @return string
     */
    public function httpEncode($content)
    {
        $header = "HTTP/1.1 200 OK\r\n";
        $header .= "Content-Type: text/html;charset=utf-8\r\n";
        $header .= "Connection: keep-alive\r\n";
        $header .= "Server: workerman/3.5.4\r\n";
        $header .= "Content-Length: " . strlen($content) . "\r\n\r\n";
        return $header . $content;
    }

    /**
     * 发送命令给主进程
     * @param $command
     */
    public function sendSignalToMaster($command)
    {
        $masterPid = file_get_contents($this->masterPidFile);
        if ($masterPid) {
            switch ($command) {
                case 'stop':
                    posix_kill($masterPid, SIGINT);
                    break;
                case 'reload':
                    posix_kill($masterPid, SIGUSR1);
                    break;
                case 'status':
                    posix_kill($masterPid, SIGUSR2);
                    sleep(1); // 等待主进程将状态信息放入文件
                    $masterStatus = file_get_contents($this->masterStatusFile);
                    echo $masterStatus;
                    unlink($this->masterStatusFile);
                    break;
            }
            exit;
        } else {
            echo "主进程不存在\n";
            exit;
        }
    }
}
Copy the code

This implements a simple HTTP server, with the main process handling signals and monitoring child processes that accept requests and process business.

test

Nginx+ php-fPM as HTTP server, both output Hello world, using apache AB tool test results are as follows:

Nginx+ php-fpm (4 Nginx worker processes and 4 php-fpm worker processes) :

Command (100 concurrent requests, 1000 requests in total) :

Ab-n1000-c100-k http://127.0.0.1:2345/Copy the code

Results:

Server Software: nginx/1.8.1 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: /index.php Document Length: 21 bytes Concurrency Level: 100 Time taken for Tests: 0.363 seconds Complete Requests: 1000 Failed Requests: 0 Keep-Alive requests: 0 Total transferred: 183000 bytes HTML transferred: 21000 bytes Requests per second: 2758.03 [#/ SEC] (mean) Time per request: 36.258 [ms] (mean) Time per request: 0.363 [MS] (mean, across all concurrent requests) Transfer rate: 492.89 [Kbytes/ SEC] ReceivedCopy the code

2. HTTP server developed by PHP (4 worker processes) :

Command (100 concurrent requests, 1000 requests in total) :

Ab-n1000-c100-k http://127.0.0.1:2345/Copy the code

Results:

Server Software: Workerman /3.5.4 Server Hostname: 127.0.0.1 Server Port: 2345 Document Path: / Document Length: 12 bytes Concurrency Level: 100 Time Taken for Tests: 0.087 seconds Complete Requests: 1000 Failed requests: 0 Keep-Alive requests: 1000 Total transferred: 140000 bytes HTML transferred: 13000 bytes Requests per second: 11506.82 [#/ SEC] (mean) Time per request: 8.691 [ms] (mean) Time per request: 0.087 [ms] (mean, across all concurrent requests) Transfer rate: 1573.20 [Kbytes/ SEC] ReceivedCopy the code

As you can see, HTTP servers developed in PHP are faster because they have fewer intermediate forwarding operations.

The above demo can be stamped here:demo

conclusion

The PHP system functions used in the above example are basically related to some of the technologies under Linux. For those interested in C implementation of TCP server, please refer to my other article: Linux Socket development simple Tcp server, the above code is only for learning to use, want to understand the relevant knowledge recommended to see a Workman source code.

feeling

PHP has always been seen as a grass-roots language, and it doesn’t matter that people think PHP doesn’t do much, but people like Workerman and Swoole are making PHP do a lot more, and it’s giving people a new understanding of PHP. Hopefully, people will be a little bit less biased and a little bit more support for PHP. Also hope our PHPer can be stronger and stronger, happy to write code.