
php自定义webServer服务器
php自定义webServer服务器,本例子是在linux上面运行的,多进程形式,不适用于windows
<?php /** * php自定义webServer服务器 * @author linyushan * @time 2017/10/12 16:49 */ /** * @socket 通信的整个过程 * @socket_create //创建套接字 * @socket_bind //绑定IP和端口 * @socket_listen //监听相应端口 * @socket_accept //接收请求 * @socket_read //获取请求内容 * @socket_write //返回数据 * @socket_close //关闭连接 */ class newTowMyServer { /** * @var newMyServer */ public static $master_worker; //主进程 public static $master_socket; //监听的socket端口套节字 resource /** * @var array 子进程 */ public static $workers; /** * 所有socket套接字数组 * @var array */ public static $allSockets = []; private $ip; private $port; private $webroot; public $count = 3; //子进程数 //将常用的MIME类型保存在一个数组中 private $contentType = array( ".html" => "text/html", ".htm" => "text/html", ".xhtml" => "text/html", ".xml" => "text/html", ".php" => "text/html", ".java" => "text/html", ".jsp" => "text/html", ".css" => "text/css", ".ico" => "image/x-icon", ".jpg" => "application/x-jpg", ".jpeg" => "image/jpeg", ".png" => "application/x-png", ".gif" => "image/gif", ".pdf" => "application/pdf", ); public function __construct($ip = "0.0.0.0", $port = 65501) { set_time_limit(0); $this->ip = $ip; $this->port = $port; $this->webroot = __DIR__ . '/www'; echo "\nServer init sucess\n"; } /** * Monitor all child processes. * 监视所有子进程。 * @return void */ public static function monitorWorkers() { while (1) { // Calls signal handlers for pending signals.调用待处理信号的信号处理程序 pcntl_signal_dispatch(); // Suspends execution of the current process until a child has exited, or until a signal is delivered //暂停执行当前进程,直到孩子退出,直到信号被传送 $status = 0; $pid = pcntl_wait($status, WUNTRACED); var_dump($pid); // Calls signal handlers for pending signals again. //再次呼叫待处理信号的信号处理程序。 pcntl_signal_dispatch(); // If a child has already exited. 如果一个孩子已经退出了。 if ($pid > 0) { //退出一个子进程,则继续开启一个子进程 unset(self::$workers[(int)$pid]); self::$master_worker->forkOneWorker(); //开启一个子进程 } else { /* // If shutdown state and all child processes exited then master process exit. if (self::$_status === self::STATUS_SHUTDOWN && !self::getAllWorkerPids()) { self::exitAndClearAll(); }*/ } } } /** * [读取get或post请求中的url,返回相应的文件] * @param [string] * @return [string] * http头 * method url protocols */ public function request($string) { echo $string; $pattern = "/\s+/"; $request = preg_split($pattern, $string); if (count($request) < 3) return "request error\n"; $filename = $this->webroot . $request[1]; echo "filename:" . $filename . "\n"; $type = $this->setContentType($filename); if (file_exists($filename)) { ini_set('display_errors', 'off'); ob_start(); include $filename; $data = ob_get_clean(); return $this->addHeader($request[2], 200, "OK", $data, $type); } else { $data = "this resource is not exists"; return $this->addHeader($request[2], 1000, "not exists", $data, $type); } } private function addHeader($protocol, $state, $desc, $str, $type) { return "{$protocol} {$state} {$desc}\r\nContent-type:{$type}\r\n" . "Content-Length:" . strlen($str) . "\r\nServer:" . self::class . "\r\n\r\n" . $str; } private function setContentType($filename) { $type = substr($filename, strpos($filename, '.')); if (isset($this->contentType[$type])) return $this->contentType[$type]; else return "text/html"; } /** * 运行多进程模式 */ public function run() { @cli_set_process_title(self::class . ' master process pid=' . posix_getpid() . ' ' . __FILE__); self::$master_worker = $this; self::$master_socket = stream_socket_server("tcp://" . $this->ip . ":" . $this->port, $errno, $errstr); if (!self::$master_socket) { echo "$errstr ($errno)<br />\n"; } stream_set_blocking(self::$master_socket, 0); //设置为非阻塞 self::$allSockets[(int)self::$master_socket] = self::$master_socket; $i = 0; while ($i < $this->count) { $this->forkOneWorker(); $i++; } } public function closeSocket($socket) { echo 'exit one socket ' . (int)$socket . "\r\n"; unset(self::$allSockets[(int)$socket]); fclose($socket); } public function forkOneWorker() { $pid = pcntl_fork(); if ($pid > 0) { self::$workers[(int)$pid] = $pid; } else { @cli_set_process_title(self::class . ' worker process pid=' . posix_getpid() . ' ' . __FILE__); while (1) { $write = $except = null; $read = self::$allSockets; echo 'blocking pid=' . posix_getpid() . "\r\n"; stream_select($read, $write, $except, NULL); //阻塞在这边,这边不判断可写的情况 foreach ($read as $index => $socket) { if ($socket === self::$master_socket) { $new_socket = stream_socket_accept($socket); //接收的新连接被别的进程处理了 if(empty($new_socket)){ continue; } self::$allSockets[(int)$new_socket] = $new_socket; } else { $string = fread($socket, 20480); if ($string === '' || $string === false) { //客户端已经退出了 $this->closeSocket($socket); continue; } $data = $this->request($string); $num = fwrite($socket, $data); if ($num == 0) { echo "WRITE ERROR:" . "\n"; } else { echo "request already succeed\n"; } $this->closeSocket($socket); } } } } } } $server = new newTowMyServer(); $server->count = 1; $server->run(); newTowMyServer:: monitorWorkers(); //监控所有进程