詳解PHP實現HTTP服務器過程
目錄
- 原生Socket編程
- 流行項目
- Workerman系
- Swoole系
- ReactPHP系
- AMPHP系
- swow
- 總結
PHP并非不能實現HTTP服務,一般來講,這叫網絡編程或Socket編程。在學習到其他語言的這部分的時候,一般的思路就是如何監聽TCP實現一個服務器,并處理HTTP協議。
PHP也可以這樣做,同時一般伴隨著高性能這樣的關鍵字出現。
原生Socket編程
我們可以通過PHP的Socket函數,很簡單的實現出HTTP服務。
function run()
{
//創建服務端的socket套接流,net協議為IPv4,protocol協議為TCP
$socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
/*綁定接收的套接流主機和端口,與客戶端相對應*/
if(socket_bind($socket,"0.0.0.0", 9502) == false){
echo "server bind fail:".socket_strerror(socket_last_error());exit();
}
//監聽套接流
if(socket_listen($socket,4)==false){
echo "server listen fail:".socket_strerror(socket_last_error());exit();
}
//非阻塞
socket_set_nonblock($socket);
call_user_func("onAccept",$socket);
}
run();
然后通過Socket處理收到的數據以及作出響應:
function onMessage($connection)
{
//拼裝返回的html內容
$content = "<html><title>hello,world</title><body>hello,world,http</body></html>";
//拼裝頭信息
$header = "";
$header .= "HTTP/1.1 200 OK\r\n";
$header .= "Date: ".gmdate("D, d M Y H:i:s T")."\r\n";
$header .= "Content-Type: text/html;charset=utf-8\r\n";
$header .= "Content-Length: ".strlen($content)."\r\n\r\n";//必須2個\r\n表示頭部信息結束
$header .= $content;
socket_write($connection,$header,strlen($header));
}
function onAccept($socket)
{
//接收客戶端傳遞過來的信息
while(true)
{
$accept_resource = socket_accept($socket);
if($accept_resource !== false)
{
$string = socket_read($accept_resource,1024);
echo "server receive is :".$string.PHP_EOL;
if($string != false)
{
call_user_func("onMessage",$accept_resource);
}
}
}
}
流行項目
實際上,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框架,支持命名空間自動加載,所以代碼像這樣:
<?php
namespace app\controller;
use support\Request;
class UserController
{
public function hello(Request $request)
{
$default_name = "webman";
// 從get請求里獲得name參數,如果沒有傳遞name參數則返回$default_name
$name = $request->get("name", $default_name);
// 向瀏覽器返回字符串
return response("hello " . $name);
}
}
除了高性能等特點,他的上手難度很低,并且風格與現代的MVC風格一致,支持PSR標準,代碼精簡高效。如果你是ThinkPHP的開發者,你會發現很容易上手Webman。
Swoole系
說道高性能HTTP服務,總是繞不開swoole的,他也是國內最早火熱起來的PHP高性能解決方案。
使用swoole實現HTTP服務的代碼也很簡單:
$http = new Swoole\Http\Server("0.0.0.0", 9501);
$http->on("Request", function ($request, $response) {
$response->header("Content-Type", "text/html; charset=utf-8");
$response->end("<h1>Hello Swoole. #" . rand(1000, 9999) . "</h1>");
});
$http->start();
swoole實際上是一個PHP的擴展,近幾年基于他發展起了很多的高性能框架,比如easyswoole、Hyperf、Swoft、MixPHP等等。它們都基于Swoole實現框架,可以很容易的創建完整度很成熟的系統。
ReactPHP系
ReactPHP 是用于 PHP 事件驅動編程的底層庫。也可以用來實現各類網絡編程,包括HTTP服務。用它實現HTTP服務也很簡單:
require __DIR__ . "/vendor/autoload.php";
$http = new React\Http\HttpServer(function (Psr\Http\Message\ServerRequestInterface $request) {
return React\Http\Message\Response::plaintext(
"Hello World!\n"
);
});
$socket = new React\Socket\SocketServer("127.0.0.1:8080");
$http->listen($socket);
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服務。
use Amp\Http\Server\RequestHandler\ClosureRequestHandler;
use Amp\Http\Server\SocketHttpServer;
use Amp\Http\Server\Request;
use Amp\Http\Server\Response;
use Amp\Http\Status;
use Amp\Socket\Server;
use Psr\Log\NullLogger;
// Run this script, then visit http://localhost:1337/ in your browser.
Amp\Loop::run(function () {
$sockets = [
Server::listen("0.0.0.0:1337"),
Server::listen("[::]:1337"),
];
$server = new SocketHttpServer($sockets, new ClosureRequestHandler(function (Request $request) {
return new Response(Status::OK, [
"content-type" => "text/plain; charset=utf-8"
], "Hello, World!");
}), new NullLogger);
yield $server->start();
// Stop the server gracefully when SIGINT is received.
// This is technically optional, but it is best to call Server::stop().
Amp\Loop::onSignal(SIGINT, function (string $watcherId) use ($server) {
Amp\Loop::cancel($watcherId);
yield $server->stop();
});
});
AMPHP也實現了很多有趣的項目,比如Mysql的客戶端,能夠實現連接池等特性。
swow
swow是一個基于協程的跨平臺并發I/O引擎,關注并發IO。
官方給出的HTTP例子代碼行數比較多,主要是展示了HTTP請求支持的每個階段的操作方法,代碼也是很簡潔的。
declare(strict_types=1);
use Swow\Buffer;
use Swow\Coroutine;
use Swow\Http\Parser;
use Swow\Http\ParserException;
use Swow\Socket;
use Swow\SocketException;
$host = getenv("SERVER_HOST") ?: "127.0.0.1";
$port = (int) (getenv("SERVER_PORT") ?: 9764);
$backlog = (int) (getenv("SERVER_BACKLOG") ?: 8192);
$multi = (bool) (getenv("SERVER_MULTI") ?: false);
$bindFlag = Socket::BIND_FLAG_NONE;
$server = new Socket(Socket::TYPE_TCP);
if ($multi) {
$server->setTcpAcceptBalance(true);
$bindFlag |= Socket::BIND_FLAG_REUSEPORT;
}
$server->bind($host, $port, $bindFlag)->listen($backlog);
while (true) {
try {
$connection = $server->accept();
} catch (SocketException $exception) {
break;
}
Coroutine::run(static function () use ($connection): void {
$buffer = new Buffer(Buffer::COMMON_SIZE);
$parser = (new Parser())->setType(Parser::TYPE_REQUEST)->setEvents(Parser::EVENT_BODY);
$parsedOffset = 0;
$body = null;
try {
while (true) {
$length = $connection->recv($buffer, $buffer->getLength());
if ($length === 0) {
break;
}
while (true) {
$parsedOffset += $parser->execute($buffer, $parsedOffset);
if ($parser->getEvent() === $parser::EVENT_NONE) {
$buffer->truncateFrom($parsedOffset);
$parsedOffset = 0;
break; /* goto recv more data */
}
if ($parser->getEvent() === Parser::EVENT_BODY) {
$body ??= new Buffer(Buffer::COMMON_SIZE);
$body->write(0, $buffer, $parser->getDataOffset(), $parser->getDataLength());
}
if ($parser->isCompleted()) {
$response = sprintf(
"HTTP/1.1 200 OK\r\n" .
"Connection: %s\r\n" .
"Content-Length: %d\r\n\r\n" .
"%s",
$parser->shouldKeepAlive() ? "Keep-Alive" : "Closed",
$body ? $body->getLength() : 0,
$body ?: ""
);
$connection->send($response);
$body?->clear();
break; /* goto recv more data */
}
}
if (!$parser->shouldKeepAlive()) {
break;
}
}
} catch (SocketException $exception) {
echo "No.{$connection->getFd()} goaway! {$exception->getMessage()}" . PHP_EOL;
} catch (ParserException $exception) {
echo "No.{$connection->getFd()} parse error! {$exception->getMessage()}" . PHP_EOL;
}
$connection->close();
});
}
總結
以上是一些非常流行的PHP框架和項目,但還有其他很多實現了高性能HTTP服務的項目。這里不多做介紹了。雖然我們談到PHP的時候,很少談到網絡編程,甚至在入門教程中根本就沒有網絡編程這節課。但是使用PHP做網絡編程的各項應用已經很火熱了。
在入門其他語言是一定有一節課程是學習網絡編程的,做PHP教程的也應該考慮考慮增加這部分課程了。
到此這篇關于詳解PHP實現HTTP服務器過程的文章就介紹到這了,更多相關PHP HTTP服務器內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!

網公網安備