当前位置:首页 > PHP教程 > php高级应用 > 列表

详解PHP实现HTTP服务器过程

发布:smiling 来源: PHP粉丝网  添加日期:2023-06-25 11:55:51 浏览: 评论:0 

一般来讲,PHP很少谈到“实现HTTP服务”的说法,因为从早期的CGI到后来的PHP-FPM,官方已经给出了最稳定的HTTP解决方案,你只要配合一个Apache或Nginx类的服务器就能实现稳定的HTTP服务。

PHP并非不能实现HTTP服务,一般来讲,这叫网络编程或Socket编程。在学习到其他语言的这部分的时候,一般的思路就是如何监听TCP实现一个服务器,并处理HTTP协议。

PHP也可以这样做,同时一般伴随着高性能这样的关键字出现。

原生Socket编程

我们可以通过PHP的Socket函数,很简单的实现出HTTP服务。

  1. function run() 
  2.     //创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP 
  3.     $socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); 
  4.     /*绑定接收的套接流主机和端口,与客户端相对应*/ 
  5.     if(socket_bind($socket,"0.0.0.0", 9502) == false){ 
  6.         echo 'server bind fail:'.socket_strerror(socket_last_error());exit(); 
  7.     } 
  8.     //监听套接流 
  9.     if(socket_listen($socket,4)==false){ 
  10.         echo 'server listen fail:'.socket_strerror(socket_last_error());exit(); 
  11.     } 
  12.     //非阻塞 
  13.     socket_set_nonblock($socket); 
  14.     call_user_func('onAccept',$socket); 
  15. run(); 

然后通过Socket处理收到的数据以及作出响应:

  1. function onMessage($connection
  2.     //拼装返回的html内容 
  3.     $content = '<html><title>hello,world</title><body>hello,world,http</body></html>'
  4.     //拼装头信息 
  5.     $header = ''
  6.     $header .= "HTTP/1.1 200 OK\r\n"
  7.     $header .= "Date: ".gmdate('D, d M Y H:i:s T')."\r\n"
  8.     $header .= "Content-Type: text/html;charset=utf-8\r\n"
  9.     $header .= "Content-Length: ".strlen($content)."\r\n\r\n";//必须2个\r\n表示头部信息结束 
  10.     $header .= $content
  11.     socket_write($connection,$header,strlen($header)); 
  12. function onAccept($socket
  13.     //接收客户端传递过来的信息 
  14.     while(true) 
  15.     { 
  16.         $accept_resource = socket_accept($socket); 
  17.         if($accept_resource !== false) 
  18.         { 
  19.             $string = socket_read($accept_resource,1024); 
  20.             echo 'server receive is :'.$string.PHP_EOL; 
  21.             if($string != false) 
  22.             { 
  23.                 call_user_func('onMessage',$accept_resource); 
  24.             } 
  25.         } 
  26.     } 

流行项目

实际上,PHP有很多在项目都在实现HTTP服务器,而且他们一般也都宣称是高性能的。

Workerman系

Workerman是一款纯PHP开发的开源高性能的PHP 应用容器。几乎能够实现任何类型的网络编程,并且内置了一个HTTP协议。

$worker = new Worker('http://0.0.0.0:1221');

Workerman的官方在21年出品了Webman,一个基于Workerman实现的高性能HTTP服务框架。替代传统PHP-FPM架构,提供高性能的HTTP服务。可以用来开发网站、接口、微服务。

Webman实际上是一个开发框架,项目的目录结构都已经设定好了,按照文档开发就行,最后只要通过命令就能运行起来。

php start.php start

Webman支持是一个MVC框架,支持命名空间自动加载,所以代码像这样:

  1. <?php 
  2. namespace app\controller; 
  3. use support\Request; 
  4. class UserController 
  5.     public function hello(Request $request
  6.     { 
  7.         $default_name = 'webman'
  8.         // 从get请求里获得name参数,如果没有传递name参数则返回$default_name 
  9.         $name = $request->get('name'$default_name); 
  10.         // 向浏览器返回字符串 
  11.         return response('hello ' . $name); 
  12.     } 

除了高性能等特点,他的上手难度很低,并且风格与现代的MVC风格一致,支持PSR标准,代码精简高效。如果你是ThinkPHP的开发者,你会发现很容易上手Webman。

Swoole系

说道高性能HTTP服务,总是绕不开swoole的,他也是国内最早火热起来的PHP高性能解决方案。

使用swoole实现HTTP服务的代码也很简单:

  1. $http = new Swoole\Http\Server('0.0.0.0', 9501); 
  2. $http->on('Request'function ($request$response) { 
  3.     $response->header('Content-Type''text/html; charset=utf-8'); 
  4.     $response->end('<h1>Hello Swoole. #' . rand(1000, 9999) . '</h1>'); 
  5. }); 
  6. $http->start(); 

swoole实际上是一个PHP的扩展,近几年基于他发展起了很多的高性能框架,比如easyswoole、Hyperf、Swoft、MixPHP等等。它们都基于Swoole实现框架,可以很容易的创建完整度很成熟的系统。

ReactPHP系

ReactPHP 是用于 PHP 事件驱动编程的底层库。也可以用来实现各类网络编程,包括HTTP服务。用它实现HTTP服务也很简单:

  1. require __DIR__ . '/vendor/autoload.php'
  2. $http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) { 
  3.     return React\Http\Message\Response::plaintext( 
  4.         "Hello World!\n" 
  5.     ); 
  6. }); 
  7. $socket = new React\Socket\SocketServer('127.0.0.1:8080'); 
  8. $http->listen($socket); 
  9. echo "Server running at http://127.0.0.1:8080" . PHP_EOL; 

它是一个底层库,一般而言,所有PSR的框架都可以基于他运行,替换PHP-FPM。所以他也提供了各个流行框架的接入方案,包括laravel、symfony等,基于ReactPHP,开发了一个PHP-PM项目。

PHP-PM 是 PHP 应用程序的进程管理器、增压器和负载平衡器。

可以直接通过命令运行:

ppm start --bootstrap=laravel --app-env=prod --debug=0 --logging=0 --workers=20

实际上ReactPHP是个很有趣的项目,比如IP电视服务器、终端shell、Mqtt的server、PHP版的Redis、一个GUI框架、比特币P2P网络等等,以后有机会给大家介绍介绍。

AMPHP系

AMPHP 是 PHP 的高质量、事件驱动库的集合,在设计时考虑了纤维和并发性。

基于AMPHP实现的HTTP服务框架叫amphp/http-server。使用它也可以快速实现一个稳定高性能的HTTP服务。

  1. use Amp\Http\Server\RequestHandler\ClosureRequestHandler; 
  2. use Amp\Http\Server\SocketHttpServer; 
  3. use Amp\Http\Server\Request; 
  4. use Amp\Http\Server\Response; 
  5. use Amp\Http\Status; 
  6. use Amp\Socket\Server; 
  7. use Psr\Log\NullLogger; 
  8. // Run this script, then visit http://localhost:1337/ in your browser. 
  9. Amp\Loop::run(function () { 
  10.     $sockets = [ 
  11.         Server::listen("0.0.0.0:1337"), 
  12.         Server::listen("[::]:1337"), 
  13.     ]; 
  14.     $server = new SocketHttpServer($socketsnew ClosureRequestHandler(function (Request $request) { 
  15.         return new Response(Status::OK, [ 
  16.             "content-type" => "text/plain; charset=utf-8" 
  17.         ], "Hello, World!"); 
  18.     }), new NullLogger); 
  19.     yield $server->start(); 
  20.     // Stop the server gracefully when SIGINT is received. 
  21.     // This is technically optional, but it is best to call Server::stop(). 
  22.     Amp\Loop::onSignal(SIGINT, function (string $watcherIduse ($server) { 
  23.         Amp\Loop::cancel($watcherId); 
  24.         yield $server->stop(); 
  25.     }); 
  26. }); 

AMPHP也实现了很多有趣的项目,比如Mysql的客户端,能够实现连接池等特性。

swow

swow是一个基于协程的跨平台并发I/O引擎,关注并发IO。

官方给出的HTTP例子代码行数比较多,主要是展示了HTTP请求支持的每个阶段的操作方法,代码也是很简洁的。

  1. declare(strict_types=1); 
  2. use Swow\Buffer; 
  3. use Swow\Coroutine; 
  4. use Swow\Http\Parser; 
  5. use Swow\Http\ParserException; 
  6. use Swow\Socket; 
  7. use Swow\SocketException; 
  8. $host = getenv('SERVER_HOST') ?: '127.0.0.1'
  9. $port = (int) (getenv('SERVER_PORT') ?: 9764); 
  10. $backlog = (int) (getenv('SERVER_BACKLOG') ?: 8192); 
  11. $multi = (bool) (getenv('SERVER_MULTI') ?: false); 
  12. $bindFlag = Socket::BIND_FLAG_NONE; 
  13. $server = new Socket(Socket::TYPE_TCP); 
  14. if ($multi) { 
  15.     $server->setTcpAcceptBalance(true); 
  16.     $bindFlag |= Socket::BIND_FLAG_REUSEPORT; 
  17. $server->bind($host$port$bindFlag)->listen($backlog); 
  18. while (true) { 
  19.     try { 
  20.         $connection = $server->accept(); 
  21.     } catch (SocketException $exception) { 
  22.         break
  23.     } 
  24.     Coroutine::run(static function () use ($connection): void { 
  25.         $buffer = new Buffer(Buffer::COMMON_SIZE); 
  26.         $parser = (new Parser())->setType(Parser::TYPE_REQUEST)->setEvents(Parser::EVENT_BODY); 
  27.         $parsedOffset = 0; 
  28.         $body = null; 
  29.         try { 
  30.             while (true) { 
  31.                 $length = $connection->recv($buffer$buffer->getLength()); 
  32.                 if ($length === 0) { 
  33.                     break
  34.                 } 
  35.                 while (true) { 
  36.                     $parsedOffset += $parser->execute($buffer$parsedOffset); 
  37.                     if ($parser->getEvent() === $parser::EVENT_NONE) { 
  38.                         $buffer->truncateFrom($parsedOffset); 
  39.                         $parsedOffset = 0; 
  40.                         break/* goto recv more data */ 
  41.                     } 
  42.                     if ($parser->getEvent() === Parser::EVENT_BODY) { 
  43.                         $body ??= new Buffer(Buffer::COMMON_SIZE); 
  44.                         $body->write(0, $buffer$parser->getDataOffset(), $parser->getDataLength()); 
  45.                     } 
  46.                     if ($parser->isCompleted()) { 
  47.                         $response = sprintf( 
  48.                             "HTTP/1.1 200 OK\r\n" . 
  49.                             "Connection: %s\r\n" . 
  50.                             "Content-Length: %d\r\n\r\n" . 
  51.                             '%s'
  52.                             $parser->shouldKeepAlive() ? 'Keep-Alive' : 'Closed'
  53.                             $body ? $body->getLength() : 0, 
  54.                             $body ?: '' 
  55.                         ); 
  56.                         $connection->send($response); 
  57.                         $body?->clear(); 
  58.                         break/* goto recv more data */ 
  59.                     } 
  60.                 } 
  61.                 if (!$parser->shouldKeepAlive()) { 
  62.                     break
  63.                 } 
  64.             } 
  65.         } catch (SocketException $exception) { 
  66.             echo "No.{$connection->getFd()} goaway! {$exception->getMessage()}" . PHP_EOL; 
  67.         } catch (ParserException $exception) { 
  68.             echo "No.{$connection->getFd()} parse error! {$exception->getMessage()}" . PHP_EOL; 
  69.         } 
  70.         $connection->close(); 
  71.     }); 

总结:

以上是一些非常流行的PHP框架和项目,但还有其他很多实现了高性能HTTP服务的项目。这里不多做介绍了。虽然我们谈到PHP的时候,很少谈到网络编程,甚至在入门教程中根本就没有网络编程这节课。但是使用PHP做网络编程的各项应用已经很火热了。

在入门其他语言是一定有一节课程是学习网络编程的,做PHP教程的也应该考虑考虑增加这部分课程了。

Tags: PHP实现HTTP服务器

分享到: