moebius/socket

基于协程的socket客户端和服务器实现。

1.0.0-RC1 2022-04-19 21:35 UTC

This package is auto-updated.

Last update: 2024-09-20 03:08:03 UTC


README

一个易于使用的接口,用于处理多个同时存在的非阻塞网络连接。

注意!PHP的默认构建限制了并发“轮询”连接的数量为1024。可以通过运行多个服务器进程或重新编译PHP来克服此限制。

架构

此库提供了三个核心类,您可以使用这些类创建网络客户端或服务器实现。

  • Moebius\Socket\Client 提供了PHP中 stream_socket_client() 函数的API,并可用于创建并发HTTP客户端。

  • Moebius\Socket\Server 提供了 stream_socket_server() 函数的API,允许您创建接受连接的服务器,并为每个新连接的客户端提供连接实例。

  • Moebius\Socket\Connection 与客户端类类似,但连接由外部发起并由服务器类返回。

示例客户端

use Moebius\Socket\Client;
use function M\{go, await};

/**
 * *** COROUTINE 1 ***
 */
$google = go(function() {
    $client = new Client('tcp://www.google.com:80');
    $client->write("GET / HTTP/1.0\r\n\r\n");
    while (!$client->eof()) {
        echo "< ".$client->readLine()."\n";
    }
});
/**
 * *** COROUTINE 2 ***
 */
$bing = go(function() {
    $client = new Client('tcp://www.bing.com:80');
    $client->write("GET / HTTP/1.0\r\n\r\n");
    while (!$client->eof()) {
        echo "< ".$client->readLine()."\n";
    }
});


/**
 * *** AWAIT BOTH COROUTINES ***
 */
await($google);
await($bing);

示例服务器

use Moebius\Socket\Server;
use function M\go;

$server = new Server('tcp://0.0.0.0:8080');
while ($connection = $server->accept()) {

    /**
     * *** LAUNCH A COROUTINE PER CONNECTION ***
     */
    go(function() use ($connection) {

        $requestLine = $connection->readLine();

        do {
            $header = $connection->readLine();
        } while ($header !== '');

        $connection->write(<<<RESPONSE
            HTTP/1.0 200 OK\r
            Content-Type: text/plain\r
            \r
            Hello World!
            RESPONSE);

        $connection->close();
            
    });
}

完整工作示例

要发出多个并发请求,您只需通过 M\go(callable $callback) 函数运行您的代码。这样,您可以同时执行许多请求。

<?php
require(__DIR__.'/../vendor/autoload.php');

use Moebius\Socket\Client;
use function M\{go, await};

// Asynchronous operation requires that you call via the `go()` function.

$futureBufferA = go(function() {

    $client = new Client('tcp://www.dagbladet.no:80');
    $client->connect();
    $client->write("GET / HTTP/1.0\r\n\r\n");

    $buffer = ''

    while (!$client->eof()) {
        $buffer .= $client->read(4096);
    }

    return $buffer;
});

$futureBufferA = go(function() {

    $client = new Client('tcp://www.vg.no:80');
    $client->connect();
    $client->write("GET / HTTP/1.0\r\n\r\n");

    $buffer = ''

    while (!$client->eof()) {
        $buffer .= $client->read(4096);
    }

    return $buffer;
});


// note that it does not matter which order you await each of these buffers
$bufferA = await( $futureBufferA );
$bufferB = await( $futureBufferB );

echo "Received ".strlen($bufferA)." bytes from client A and ".strlen($bufferB)." bytes from client B\n";

API

  • Client::__construct(string $address, ClientOptions|array $options=[])。创建一个socket客户端实例,但不会执行任何网络操作。

  • Client::connect()。非阻塞连接到服务器。(DNS查找操作可能会暂时阻塞)

  • Client::disconnect()。关闭连接。

  • Client::read(int $length): string。从服务器读取 $length 字节。如果您想发出多个请求,可能需要通过 M\go(callable $coroutine) 函数执行此操作。