1. 什么是长连接通信
在 HTTP 请求中,客户端和服务器之间的通信通常是短暂的,也就是说一旦服务器返回了相应的结果,连接就会被关闭。这种方式在某些场景下并不适用,比如在线聊天,游戏等需要持续通信的场景。长连接就是一种可以保持持续通信的方式。长连接也叫做 HTTP 长轮询 或者 Comet 技术。
2. PHP 中的长连接实现
2.1 使用 socket 实现
PHP 中可以使用 socket 函数库来实现长连接。使用 socket 函数库可以建立一个服务器端和一个客户端之间的独立连接,当有数据传入或者传出时,连接将会被激活。
// 建立服务器端
$server = 'localhost';
$port = 9999;
$max_clients = 10;
if(!($sock = socket_create(AF_INET, SOCK_STREAM, 0))){
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Couldn't create socket: [$errorcode] $errormsg \n");
}
echo "Socket created";
if(!socket_bind($sock, $server , $port)){
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Could not bind socket : [$errorcode] $errormsg \n");
}
echo "Socket bind successful";
if(!socket_listen($sock , $max_clients)){
$errorcode = socket_last_error();
$errormsg = socket_strerror($errorcode);
die("Could not listen on socket : [$errorcode] $errormsg \n");
}
echo "Socket listen successful";
while (true) {
$client = socket_accept($sock);
// 在这里进行数据的交互操作
socket_close($client);
}
socket_close($sock);
上面的代码创建了一个服务器并且监听了端口号为 9999 的连接,等待客户端的连接请求。一旦有连接建立成功,服务器将会创建一个新的 socket 连接和客户端进行通信。当通信结束后,服务器将会关闭这个连接。
2.2 使用 Websocket 实现
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 不同于 HTTP,其通信双方可以在任何时候主动发送或接收数据,并且没有 HTTP 请求头的限制。而且 WebSocket 还有传输层加密和压缩等功能。
PHP中可用使用开源库 Ratchet 来实现 WebSocket。Ratchet 能够提供底层的通讯支持,同时还可以和 PHP 应用程序进行集成。
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
require dirname(__DIR__) . '/vendor/autoload.php';
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "New connection! ({$conn->resourceId})\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
foreach ($this->clients as $client) {
if ($client !== $from) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "Connection {$conn->resourceId} has disconnected\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "An error has occurred: {$e->getMessage()}\n";
$conn->close();
}
}
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
$server->run();
上面的代码实现了一个简单的聊天室应用。它可以让使用者在不同的客户端中进行聊天,所有的消息都将在客户端之间实时进行交互。
2.3 使用 Swoole 实现
Swoole 是一个为 PHP 提供的高性能网络框架,可以轻易地实现高并发、异步任务,以及长连接等需求。
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);
$server->on("open", function (Swoole\WebSocket\Server $server, $request) {
echo "server: handshake success with fd{$request->fd}\n";
});
$server->on("message", function (Swoole\WebSocket\Server $server, $frame) {
echo "receive from {$frame->fd}: {$frame->data}, opcode: {$frame->opcode}, fin: {$frame->finish}\n";
$server->push($frame->fd, "this is server");
});
$server->on("close", function (Swoole\WebSocket\Server $server, $fd) {
echo "client {$fd} closed\n";
});
$server->start();
上面的代码实现了一个简单的 WebSocket 服务,客户端只需要建立一个与服务端的长连接,就可以向服务端发送任意的消息,并接收到服务端的响应。
3. 小结
在本文中,我们介绍了长连接通信的概念,以及如何在 PHP 中使用 socket、WebSocket 以及 Swoole 等库来实现长连接。