[PHP]stream_socket_acceptでTCPサーバーを実装する|接続受け入れからデータ送受信までの実践ガイド

PHP

はじめに

PHPでTCPサーバーを実装する際、クライアントからの接続を「受け入れる」処理が必要になります。stream_socket_server でリスニングソケットを作成した後、stream_socket_accept を呼び出すことで、実際の通信相手との接続ストリームを取得できます。

stream_socket_accept は、待ち受けているサーバーソケットに対して接続してきたクライアントを受け付け、その接続に対応したストリームリソースを返す関数です。HTTPサーバー、チャットサーバー、プロキシなど、あらゆるTCPサーバーの中核を担います。

この記事では、基本的な使い方から実践的なサーバー実装まで、クラスを用いた具体例とともに丁寧に解説します。


stream_socket_accept とは

項目内容
関数名stream_socket_accept
PHPバージョンPHP 5.0.0以降
カテゴリストリーム関数
返り値resource(成功時)、false(失敗・タイムアウト時)

構文

stream_socket_accept(
    resource $socket,
    ?float   $timeout    = null,
    string   &$peer_name = ''
): resource|false

パラメータ

パラメータ説明
$socketresourcestream_socket_server で作成したサーバーソケット
$timeout?floatタイムアウト秒数。null でデフォルト(default_socket_timeout ini値)、0 で即時返却
&$peer_namestring接続してきたクライアントのアドレスが格納される(例:127.0.0.1:54321

返り値

意味
resource接続済みのクライアントストリーム
falseタイムアウト、またはエラー

サーバー/クライアントの関係

【stream_socket_server と stream_socket_accept の役割分担】

サーバー側                              クライアント側
─────────────────────────────────────────────────────
stream_socket_server('tcp://0.0.0.0:8080')
  └── リスニングソケット作成(待ち受け開始)

stream_socket_accept($server)           stream_socket_client('tcp://127.0.0.1:8080')
  └── 接続を受け入れ                    └── サーバーに接続
       ↓
  クライアントとの通信ストリームを返す
       ↓
  fread / fwrite で送受信

基本的な使い方

<?php
// サーバーソケットを作成
$server = stream_socket_server('tcp://0.0.0.0:8080', $errno, $errstr);

if (!$server) {
    die("サーバー起動失敗 [{$errno}]: {$errstr}");
}

echo "8080番ポートで待ち受け中..." . PHP_EOL;

// クライアントの接続を受け付ける(タイムアウト30秒)
$client = stream_socket_accept($server, 30, $peerName);

if ($client === false) {
    echo "タイムアウトまたは接続エラー" . PHP_EOL;
} else {
    echo "接続元: {$peerName}" . PHP_EOL;

    fwrite($client, "接続しました!\n");
    $data = fread($client, 1024);
    echo "受信: {$data}";

    fclose($client);
}

fclose($server);

実践例(クラスを使った実装)

例1:シンプルなエコーサーバー

受け取ったデータをそのまま返す、最もシンプルなTCPサーバーです。

<?php

class EchoServer
{
    private $server;
    private string $address;
    private bool   $running = false;

    public function __construct(string $host = '127.0.0.1', int $port = 8080)
    {
        $this->address = "tcp://{$host}:{$port}";

        $this->server = stream_socket_server(
            $this->address,
            $errno,
            $errstr,
            STREAM_SERVER_BIND | STREAM_SERVER_LISTEN
        );

        if (!$this->server) {
            throw new RuntimeException("サーバー起動失敗 [{$errno}]: {$errstr}");
        }

        echo "エコーサーバー起動: {$this->address}" . PHP_EOL;
    }

    public function run(int $maxConnections = 5): void
    {
        $this->running = true;
        $handled       = 0;

        while ($this->running && $handled < $maxConnections) {
            // タイムアウト5秒でクライアントを待つ
            $client = stream_socket_accept($this->server, 5, $peerName);

            if ($client === false) {
                echo "タイムアウト(次の接続を待機中)" . PHP_EOL;
                continue;
            }

            echo "[{$peerName}] 接続" . PHP_EOL;
            $this->handleClient($client, $peerName);
            $handled++;
        }
    }

    private function handleClient(resource $client, string $peerName): void
    {
        stream_set_timeout($client, 5);

        while (!feof($client)) {
            $data = fread($client, 4096);

            $meta = stream_get_meta_data($client);
            if ($meta['timed_out']) {
                echo "[{$peerName}] タイムアウト" . PHP_EOL;
                break;
            }

            if ($data === false || $data === '') {
                break;
            }

            echo "[{$peerName}] 受信: " . trim($data) . PHP_EOL;

            // そのままエコーバック
            fwrite($client, $data);
        }

        echo "[{$peerName}] 切断" . PHP_EOL;
        fclose($client);
    }

    public function stop(): void
    {
        $this->running = false;
        fclose($this->server);
    }
}

// ※ このサーバーは実際に起動すると待ち受け状態になります
// $server = new EchoServer('127.0.0.1', 8080);
// $server->run(maxConnections: 3);
echo "EchoServer クラス定義完了(実行するにはコメントを外してください)" . PHP_EOL;

例2:HTTPレスポンスを返す最小限のHTTPサーバー

stream_socket_accept を使って、ブラウザからアクセス可能な簡易HTTPサーバーを実装します。

<?php

class MinimalHttpServer
{
    private $server;
    private array $routes  = [];
    private int   $port;

    public function __construct(int $port = 8080)
    {
        $this->port   = $port;
        $this->server = stream_socket_server(
            "tcp://0.0.0.0:{$port}",
            $errno,
            $errstr
        );

        if (!$this->server) {
            throw new RuntimeException("起動失敗 [{$errno}]: {$errstr}");
        }
    }

    public function get(string $path, callable $handler): void
    {
        $this->routes['GET'][$path] = $handler;
    }

    public function run(int $maxRequests = 10): void
    {
        echo "HTTP サーバー起動: http://127.0.0.1:{$this->port}/" . PHP_EOL;

        for ($i = 0; $i < $maxRequests; $i++) {
            $client = stream_socket_accept($this->server, 30, $peerName);

            if ($client === false) {
                continue;
            }

            stream_set_timeout($client, 5);
            $this->handleRequest($client, $peerName);
        }

        fclose($this->server);
    }

    private function handleRequest(resource $client, string $peerName): void
    {
        $rawRequest = '';
        while (!feof($client)) {
            $rawRequest .= fread($client, 4096);
            // ヘッダー終端(空行)を検知
            if (str_contains($rawRequest, "\r\n\r\n")) {
                break;
            }
            $meta = stream_get_meta_data($client);
            if ($meta['timed_out']) {
                break;
            }
        }

        [$method, $path] = $this->parseRequestLine($rawRequest);
        echo "[{$peerName}] {$method} {$path}" . PHP_EOL;

        $handler = $this->routes[$method][$path] ?? null;

        if ($handler) {
            [$statusCode, $body] = $handler($path);
        } else {
            $statusCode = 404;
            $body       = "<h1>404 Not Found</h1><p>{$path} は存在しません</p>";
        }

        $response = $this->buildResponse($statusCode, $body);
        fwrite($client, $response);
        fclose($client);
    }

    private function parseRequestLine(string $raw): array
    {
        $lines = explode("\r\n", $raw);
        $parts = explode(' ', $lines[0] ?? '');
        return [$parts[0] ?? 'GET', $parts[1] ?? '/'];
    }

    private function buildResponse(int $status, string $body): string
    {
        $statusTexts = [200 => 'OK', 404 => 'Not Found', 500 => 'Internal Server Error'];
        $text        = $statusTexts[$status] ?? 'Unknown';
        $length      = strlen($body);

        return implode("\r\n", [
            "HTTP/1.1 {$status} {$text}",
            "Content-Type: text/html; charset=UTF-8",
            "Content-Length: {$length}",
            "Connection: close",
            "",
            $body,
        ]);
    }
}

// ルート定義
$http = new MinimalHttpServer(8080);

$http->get('/', fn($path) => [200, '<h1>トップページ</h1><p>PHPサーバーへようこそ!</p>']);
$http->get('/hello', fn($path) => [200, '<h1>Hello, World!</h1>']);
$http->get('/time', fn($path) => [200, '<p>現在時刻: ' . date('Y-m-d H:i:s') . '</p>']);

// $http->run(); // 実際に起動する場合
echo "MinimalHttpServer クラス定義完了" . PHP_EOL;
echo "起動すると http://127.0.0.1:8080/ でアクセス可能" . PHP_EOL;

例3:タイムアウトとリトライを管理するアクセプターラッパー

stream_socket_accept のタイムアウト挙動をラップして、堅牢な接続受け付けを実現します。

<?php

class RobustSocketAcceptor
{
    private $server;
    private float  $acceptTimeout;
    private int    $maxTimeouts;
    private array  $acceptLog = [];

    public function __construct(
        resource $server,
        float    $acceptTimeout = 5.0,
        int      $maxTimeouts   = 3
    ) {
        $this->server        = $server;
        $this->acceptTimeout = $acceptTimeout;
        $this->maxTimeouts   = $maxTimeouts;
    }

    /**
     * 接続を待ち受け、タイムアウトが続いた場合は null を返す
     */
    public function accept(): ?array
    {
        $timeoutCount = 0;

        while ($timeoutCount < $this->maxTimeouts) {
            $startTime = microtime(true);
            $peerName  = '';

            $client = stream_socket_accept(
                $this->server,
                $this->acceptTimeout,
                $peerName
            );

            $elapsed = microtime(true) - $startTime;

            if ($client !== false) {
                $this->acceptLog[] = [
                    'result'     => 'accepted',
                    'peer'       => $peerName,
                    'elapsed_ms' => round($elapsed * 1000, 2),
                    'time'       => date('H:i:s'),
                ];

                return ['stream' => $client, 'peer' => $peerName];
            }

            // false = タイムアウトまたはエラー
            $timeoutCount++;
            $this->acceptLog[] = [
                'result'     => 'timeout',
                'peer'       => '',
                'elapsed_ms' => round($elapsed * 1000, 2),
                'time'       => date('H:i:s'),
            ];

            echo "タイムアウト {$timeoutCount}/{$this->maxTimeouts}回目({$this->acceptTimeout}秒待機)" . PHP_EOL;
        }

        return null; // 最大タイムアウト到達
    }

    public function printLog(): void
    {
        echo "=== アクセプトログ ===" . PHP_EOL;
        foreach ($this->acceptLog as $i => $entry) {
            $icon = $entry['result'] === 'accepted' ? '✓' : '⏱';
            $peer = $entry['peer'] ? " from {$entry['peer']}" : '';
            echo "  [{$entry['time']}] {$icon} {$entry['result']}{$peer} ({$entry['elapsed_ms']}ms)" . PHP_EOL;
        }
    }
}

// 使用例(デモ用:即時タイムアウトで動作確認)
$server = stream_socket_server('tcp://127.0.0.1:18080', $errno, $errstr);

if ($server) {
    $acceptor = new RobustSocketAcceptor(
        server:        $server,
        acceptTimeout: 0.1, // 100msで即タイムアウト(デモ用)
        maxTimeouts:   3
    );

    $result = $acceptor->accept();

    if ($result === null) {
        echo "接続なし(最大タイムアウト到達)" . PHP_EOL;
    } else {
        echo "接続受け付け: {$result['peer']}" . PHP_EOL;
        fclose($result['stream']);
    }

    $acceptor->printLog();
    fclose($server);
}

出力例:

タイムアウト 1/3回目(0.1秒待機)
タイムアウト 2/3回目(0.1秒待機)
タイムアウト 3/3回目(0.1秒待機)
接続なし(最大タイムアウト到達)
=== アクセプトログ ===
  [12:00:01] ⏱ timeout (100.12ms)
  [12:00:01] ⏱ timeout (100.08ms)
  [12:00:01] ⏱ timeout (100.11ms)

例4:stream_select と組み合わせたノンブロッキングサーバー

stream_select でリスニングソケットを監視し、接続があったときだけ stream_socket_accept を呼ぶことでブロッキングを最小化します。

<?php

class NonBlockingServer
{
    private $server;
    private array  $clients   = [];
    private array  $peerNames = [];
    private int    $maxClients;

    public function __construct(string $address, int $maxClients = 10)
    {
        $this->maxClients = $maxClients;
        $this->server     = stream_socket_server($address, $errno, $errstr);

        if (!$this->server) {
            throw new RuntimeException("起動失敗 [{$errno}]: {$errstr}");
        }

        // サーバーソケットをノンブロッキングに設定
        stream_set_blocking($this->server, false);
        echo "ノンブロッキングサーバー起動: {$address}" . PHP_EOL;
    }

    public function tick(): void
    {
        // 監視するソケット一覧(サーバー + 全クライアント)
        $readSockets = array_merge([$this->server], $this->clients);
        $write  = null;
        $except = null;

        // 変化があるまで最大100ms待機
        $changed = stream_select($readSockets, $write, $except, 0, 100_000);

        if ($changed === false || $changed === 0) {
            return;
        }

        foreach ($readSockets as $sock) {
            if ($sock === $this->server) {
                // 新規接続
                $this->acceptNew();
            } else {
                // 既存クライアントからデータ
                $this->handleClientData($sock);
            }
        }
    }

    private function acceptNew(): void
    {
        if (count($this->clients) >= $this->maxClients) {
            // 上限に達しているので即時accept→拒否
            $client = stream_socket_accept($this->server, 0);
            if ($client) {
                fwrite($client, "サーバーが満員です\n");
                fclose($client);
            }
            return;
        }

        $client = stream_socket_accept($this->server, 0, $peerName);

        if ($client !== false) {
            stream_set_blocking($client, false);
            $id                   = (int) $client;
            $this->clients[$id]   = $client;
            $this->peerNames[$id] = $peerName;
            echo "[接続] {$peerName} (ID:{$id}, 合計:" . count($this->clients) . ")" . PHP_EOL;
        }
    }

    private function handleClientData(resource $sock): void
    {
        $id   = (int) $sock;
        $peer = $this->peerNames[$id] ?? 'unknown';
        $data = fread($sock, 4096);

        if ($data === false || $data === '' || feof($sock)) {
            echo "[切断] {$peer}" . PHP_EOL;
            fclose($sock);
            unset($this->clients[$id], $this->peerNames[$id]);
            return;
        }

        echo "[受信] {$peer}: " . trim($data) . PHP_EOL;

        // 全クライアントにブロードキャスト
        foreach ($this->clients as $cid => $client) {
            fwrite($client, "[{$peer}]: {$data}");
        }
    }

    public function getClientCount(): int
    {
        return count($this->clients);
    }

    public function shutdown(): void
    {
        foreach ($this->clients as $client) {
            fclose($client);
        }
        fclose($this->server);
        echo "サーバーシャットダウン" . PHP_EOL;
    }
}

// 使用例(10回tickして終了するデモ)
$srv = new NonBlockingServer('tcp://127.0.0.1:18081');

for ($i = 0; $i < 10; $i++) {
    $srv->tick();
    echo "tick {$i} 完了(接続数: {$srv->getClientCount()})" . PHP_EOL;
}

$srv->shutdown();

出力例(接続がない場合):

ノンブロッキングサーバー起動: tcp://127.0.0.1:18081
tick 0 完了(接続数: 0)
tick 1 完了(接続数: 0)
...
tick 9 完了(接続数: 0)
サーバーシャットダウン

例5:TLSサーバー ─ SSL/TLSを使った暗号化接続を受け付ける

stream_socket_serverssl:// スキームを指定し、暗号化された接続を受け付けます。

<?php

class TlsServer
{
    private $server;
    private string $certFile;
    private string $keyFile;

    public function __construct(
        string $host,
        int    $port,
        string $certFile,
        string $keyFile
    ) {
        $this->certFile = $certFile;
        $this->keyFile  = $keyFile;

        $context = stream_context_create([
            'ssl' => [
                'local_cert'        => $certFile,
                'local_pk'          => $keyFile,
                'verify_peer'       => false,
                'verify_peer_name'  => false,
                'allow_self_signed' => true,
            ],
        ]);

        $this->server = stream_socket_server(
            "ssl://{$host}:{$port}",
            $errno,
            $errstr,
            STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,
            $context
        );

        if (!$this->server) {
            throw new RuntimeException("TLSサーバー起動失敗 [{$errno}]: {$errstr}");
        }

        echo "TLSサーバー起動: ssl://{$host}:{$port}" . PHP_EOL;
    }

    public function acceptOne(float $timeout = 30.0): void
    {
        $client = stream_socket_accept($this->server, $timeout, $peerName);

        if ($client === false) {
            echo "タイムアウト" . PHP_EOL;
            return;
        }

        echo "TLS接続受け付け: {$peerName}" . PHP_EOL;

        // TLSネゴシエーション情報を取得
        $meta = stream_get_meta_data($client);
        $crypto = isset($meta['crypto']) ? $meta['crypto'] : [];

        if (!empty($crypto)) {
            echo "暗号化プロトコル: " . ($crypto['protocol']   ?? 'unknown') . PHP_EOL;
            echo "暗号スイート    : " . ($crypto['cipher_name'] ?? 'unknown') . PHP_EOL;
        }

        fwrite($client, "TLS接続を受け付けました\r\n");
        fclose($client);
    }

    public function close(): void
    {
        if (is_resource($this->server)) {
            fclose($this->server);
        }
    }
}

// 証明書ファイルが必要なため、ここではクラス定義のみ
// 実際の使用例:
// $tls = new TlsServer('0.0.0.0', 8443, '/path/to/cert.pem', '/path/to/key.pem');
// $tls->acceptOne(30.0);
// $tls->close();

echo "TlsServer クラス定義完了(証明書ファイルを用意して使用してください)" . PHP_EOL;

例6:接続情報を詳細に記録するコネクションロガー

接続元IP・ポート・接続時刻・通信量をすべて記録し、サーバーの通信を可視化します。

<?php

class ConnectionLogger
{
    private array $connections = [];
    private int   $totalAccepted = 0;

    /**
     * stream_socket_accept の結果を記録してラップする
     */
    public function accept(resource $server, float $timeout = 5.0): ?array
    {
        $peerName = '';
        $client   = stream_socket_accept($server, $timeout, $peerName);

        if ($client === false) {
            return null;
        }

        $this->totalAccepted++;
        $id = $this->totalAccepted;

        // ローカル側のアドレスも取得
        $localName = stream_socket_get_name($client, false);

        $connInfo = [
            'id'         => $id,
            'peer'       => $peerName,
            'local'      => $localName,
            'connected_at' => microtime(true),
            'bytes_read'   => 0,
            'bytes_written'=> 0,
            'closed_at'  => null,
        ];

        $this->connections[$id] = &$connInfo;

        echo "[{$connInfo['id']}] 接続: {$peerName} → {$localName}" . PHP_EOL;

        return ['stream' => $client, 'id' => $id, 'info' => &$connInfo];
    }

    public function recordRead(int $id, int $bytes): void
    {
        if (isset($this->connections[$id])) {
            $this->connections[$id]['bytes_read'] += $bytes;
        }
    }

    public function recordWrite(int $id, int $bytes): void
    {
        if (isset($this->connections[$id])) {
            $this->connections[$id]['bytes_written'] += $bytes;
        }
    }

    public function closeConnection(int $id, resource $client): void
    {
        if (isset($this->connections[$id])) {
            $this->connections[$id]['closed_at'] = microtime(true);
        }
        fclose($client);
    }

    public function printReport(): void
    {
        echo PHP_EOL . "=== 接続レポート ===" . PHP_EOL;
        echo str_pad("ID", 5)
           . str_pad("接続元",           24)
           . str_pad("受信",             12)
           . str_pad("送信",             12)
           . "接続時間" . PHP_EOL;
        echo str_repeat('-', 62) . PHP_EOL;

        foreach ($this->connections as $conn) {
            $duration = $conn['closed_at']
                ? round(($conn['closed_at'] - $conn['connected_at']) * 1000, 1) . 'ms'
                : '接続中';

            echo str_pad($conn['id'],           5)
               . str_pad($conn['peer'],          24)
               . str_pad($conn['bytes_read']  . 'B', 12)
               . str_pad($conn['bytes_written'] . 'B', 12)
               . $duration . PHP_EOL;
        }

        echo PHP_EOL . "合計接続数: {$this->totalAccepted}" . PHP_EOL;
    }
}

// 使用例(ループで複数接続をシミュレート)
$server = stream_socket_server('tcp://127.0.0.1:18082', $errno, $errstr);

if ($server) {
    $logger = new ConnectionLogger();

    // 自前でクライアント接続をシミュレート(fork不使用)
    $testClients = [];
    for ($i = 0; $i < 3; $i++) {
        $tc = stream_socket_client('tcp://127.0.0.1:18082', $e, $es, 1);
        if ($tc) {
            $testClients[] = $tc;
        }
    }

    // 接続を受け付けてロギング
    for ($i = 0; $i < count($testClients); $i++) {
        $conn = $logger->accept($server, 0.5);
        if ($conn) {
            $bytes = fwrite($conn['stream'], "Hello Client!\n");
            $logger->recordWrite($conn['id'], $bytes ?: 0);

            $data = fread($conn['stream'], 256);
            $logger->recordRead($conn['id'], strlen($data ?: ''));

            $logger->closeConnection($conn['id'], $conn['stream']);
        }
    }

    foreach ($testClients as $tc) {
        fclose($tc);
    }

    fclose($server);
    $logger->printReport();
}

出力例:

[1] 接続: 127.0.0.1:52341 → 127.0.0.1:18082
[2] 接続: 127.0.0.1:52342 → 127.0.0.1:18082
[3] 接続: 127.0.0.1:52343 → 127.0.0.1:18082

=== 接続レポート ===
ID   接続元                  受信        送信        接続時間
--------------------------------------------------------------
1    127.0.0.1:52341         0B          14B         1.2ms
2    127.0.0.1:52342         0B          14B         0.9ms
3    127.0.0.1:52343         0B          14B         1.1ms

合計接続数: 3

関連する関数との比較

関数役割
stream_socket_serverリスニングソケットを作成する
stream_socket_acceptクライアントの接続を受け付ける
stream_socket_clientサーバーに接続する(クライアント側)
stream_socket_get_nameソケットのローカル/リモートアドレスを取得
stream_socket_sendto接続なしでデータを送信(UDP向け)
stream_select複数ソケットを効率よく監視する

stream_socket_accept vs socket_accept

// stream_socket_accept: ストリームAPI(fread/fwrite で扱える)
$server = stream_socket_server('tcp://0.0.0.0:8080');
$client = stream_socket_accept($server, 30);
fwrite($client, "Hello\n");            // fwrite が使える

// socket_accept: ソケット拡張API(socket_read/socket_write が必要)
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($server, '0.0.0.0', 8080);
socket_listen($server);
$client = socket_accept($server);
socket_write($client, "Hello\n");      // socket_write が必要
観点stream_socket_acceptsocket_accept
所属ストリーム関数ソケット拡張
読み書きfread / fwritesocket_read / socket_write
SSL/TLSコンテキストで対応可別途設定が必要
推奨度◎(モダンなPHPで推奨)△(低レベル操作が必要な場合)

よくある注意点・落とし穴

1. $timeout = 0 は「即時返却」を意味する

0 を指定すると接続がなければ即座に false を返します。stream_select と組み合わせるノンブロッキングサーバーで使います。

// NG:0 を「無限待機」と勘違いしてしまう
$client = stream_socket_accept($server, 0); // 即座に false が返る場合あり

// 無限待機したい場合は null または -1
$client = stream_socket_accept($server, -1);  // 接続が来るまで無限に待つ

2. クライアントストリームも必ず閉じる

fclose しないとファイルディスクリプタが枯渇します。

$client = stream_socket_accept($server, 30);
if ($client) {
    // 処理...
    fclose($client); // 必須
}

3. サーバーソケットとクライアントソケットの区別

stream_socket_accept が返すのはクライアントとの通信用ストリームです。サーバーソケット(リスニングソケット)とは別物で、読み書きはクライアントストリームに対して行います。

$server = stream_socket_server('tcp://0.0.0.0:8080'); // リスニング用(fwriteしない)
$client = stream_socket_accept($server, 30);           // 通信用(fwrite/freadはこちら)

fwrite($server, "NG"); // サーバーソケットに書いても届かない
fwrite($client, "OK"); // クライアントに正しく届く

4. UDPでは使えない

stream_socket_accept はTCP(接続型)専用です。UDPのデータ受信には stream_socket_recvfrom を使います。

// TCP → stream_socket_accept が使える
$server = stream_socket_server('tcp://0.0.0.0:8080');
$client = stream_socket_accept($server, 30); // OK

// UDP → stream_socket_accept は使えない
$server = stream_socket_server('udp://0.0.0.0:8080');
$data   = stream_socket_recvfrom($server, 1024, 0, $peer); // UDPはこちら

まとめ

項目内容
関数名stream_socket_accept(resource $socket, ?float $timeout, string &$peer_name): resource|false
主な用途TCPサーバーでクライアント接続を受け付ける
セットで使う関数stream_socket_serverstream_selectstream_socket_get_name
$timeout = 0即時返却(接続なければ false
$timeout = -1接続まで無限待機
UDP対応不可(UDP は stream_socket_recvfrom を使う)
PHP バージョンPHP 5.0.0 以上

stream_socket_accept は、PHPでTCPサーバーを実装する際の核となる関数です。stream_socket_server と組み合わせてリスニングを行い、stream_select を加えることでノンブロッキングな多重接続サーバーへと発展させられます。

SSL/TLSもコンテキスト設定だけで対応でき、fread / fwrite という馴染みのあるAPIで通信できる点が、低レベルなソケット拡張に比べた大きなメリットです。ぜひ実際のサーバー実装で活用してみてください。

タイトルとURLをコピーしました