🎃

PHPでソケット通信の練習をしてみた。

2024/12/14に公開

まえがき

みなさんはプログラミングしていて通信処理を使うことはありますか?
PHPプログラマーならほぼ全員HTTP通信を経験していると思いますし、最近はjavascriptプログラマーもAjax通信を使えて当たり前になってきていると思います。

しかしwebsocketやソケット通信となると誰でもってことはないのではないでしょうか?
少なくとも私はつい最近までソケット通信を触ることはありませんでした。

PHPで簡単な実験をやってみたので紹介します。

それぞれの違いをざっくりと。

通信方式 どう動くの? つながり方 何に使われる?
HTTP通信 「これください!」→「はい、どうぞ!」の1回限りの会話 一回ごとに接続を作ってすぐ切る Webサイトの表示やAPIのやり取り
Socket通信 電話みたいに「つなぎっぱなし」で自由に会話できる 必要な間だけつないでやり取りする ゲーム通信やカスタムデータのやり取り
WebSocket通信 「ずっとつなぎっぱなし」でチャットみたいな会話が可能 常に接続を維持する リアルタイムチャットや通知、ゲーム

繋ぎっぱなしにできるから、高速通信も可能になるってわけですね。
またSocket通信ではつなぎ方も選べるなどカスタムに富んでいます。
パソコン以外の装置同士でのやり取りで活躍していたりします。

実演

では、実際のコードです。
ソケット通信ではサーバからもクライアントへメッセージを送れるのですが、とはいえどっちから繋ぎにいくかはあります。

サーバが待機して、クライアントがつなぎにいくって概念はHTTPと同じですね。

サーバ側コード

server.php
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    die("ソケットの作成に失敗: " . socket_strerror(socket_last_error()) . "\n");
}

if (socket_bind($socket, '127.0.0.1', 12345) === false) {
    die("バインドに失敗: " . socket_strerror(socket_last_error($socket)) . "\n");
}

if (socket_listen($socket) === false) {
    die("リスンに失敗: " . socket_strerror(socket_last_error($socket)) . "\n");
}

echo "サーバー待機中...\n";

do {
    $client = socket_accept($socket);
    if ($client === false) {
        echo "クライアント接続失敗: " . socket_strerror(socket_last_error($socket)) . "\n";
        break;
    }

    // クライアントからの接続が持続している間応答を返し続ける。
    while (true) {
        $data = socket_read($client, 1024);

        // 切断されるとsocket_readはfalseか空文字を返してくる。
        if ($data == false || $data === "") {
            echo "クライアントから切断されました。\n";
            break;
        }

        echo "受信: $data\n";
        $response = "Hello, Client!" . date('Y-m-d H:i:s');
        socket_write($client, $response, strlen($response));
    }


    socket_close($client);
} while (true);

socket_close($socket);

クライアント側コード

client.php
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if ($socket === false) {
    die("ソケットの作成に失敗: " . socket_strerror(socket_last_error()) . "\n");
}

$result = socket_connect($socket, '127.0.0.1', 12345);
if ($result === false) {
    die("接続に失敗: " . socket_strerror(socket_last_error($socket)) . "\n");
}

echo "サーバーに接続しました。メッセージを入力してください('exit'で終了):\n";

// 繰り返しメッセージを送信
while (true) {
    // キーボード入力を取得
    echo "入力: ";
    $message = trim(fgets(STDIN)); // 標準入力から文字列を取得して余分な空白を削除

    if ($message === "exit") {
        echo "接続を終了します。\n";
        break;
    }

    // サーバーにメッセージを送信
    socket_write($socket, $message, strlen($message));

    // サーバーからの応答を受信
    $response = socket_read($socket, 1024);
    if ($response === false) {
        echo "サーバーからの応答がありません。\n";
    } else {
        echo "受信: $response\n";
    }
}

socket_close($socket);

動かす。

ターミナルを2枚立ち上げて、それぞれで、サーバ側、クライアント側のPHPを動かします。

ターミナルA
php server.php
ターミナルB
php client.php

ターミナルB側でテキストを入力するとターミナルAに届いて応答が返ってくるのがわかると思います。
簡単に1台のパソコンで実演しましたが、実際にはこのターミナルを別のパソコンで開いたりして行います。

おわりに

いかがだったでしょうか?
正直私もあまり詳しくはないのですが、やってみたらなかなか面白かったです。
この記事のコードをコピーするだけで実験できるので興味ある方はやってみてください。
※業務レベルで行うにはスレッドと組み合わせないと処理がブロックされてしまいます。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion