PHP如何实现一个完整的HTTP服务器功能?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1342个文字,预计阅读时间需要6分钟。
目录 + 原生Socket编程 + 流行项目 + Workerman系列 + Swoole系列 + ReactPHP系列 + AMPHP系列 + swow + 总结 + PHP并发不能实现HTTP服务,通常来讲,这就是网络编程或Socket编程。在学习其他语言的这部分内容时,一般思路是:
目录
- 原生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,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服务的代码也很简单:
$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也实现了很多有趣的项目,比如Mysql的客户端,能够实现连接池等特性。 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服务器内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!amphp/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();
});
});
swow
总结
本文共计1342个文字,预计阅读时间需要6分钟。
目录 + 原生Socket编程 + 流行项目 + Workerman系列 + Swoole系列 + ReactPHP系列 + AMPHP系列 + swow + 总结 + PHP并发不能实现HTTP服务,通常来讲,这就是网络编程或Socket编程。在学习其他语言的这部分内容时,一般思路是:
目录
- 原生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,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服务的代码也很简单:
$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也实现了很多有趣的项目,比如Mysql的客户端,能够实现连接池等特性。 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服务器内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!amphp/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();
});
});
swow
总结

