はじめに
TCPサーバーやクライアントを実装する際、「今どこと通信しているのか」「自分のIPとポートは何番か」を知りたい場面は多くあります。ログ記録・アクセス制限・デバッグなど、あらゆる場面でソケットのアドレス情報が必要になります。
stream_socket_get_name は、ストリームソケットの ローカル側またはリモート側のアドレスとポート を文字列で返す関数です。サーバー・クライアントを問わず、接続に関わるすべてのストリームで使えます。
この記事では、基本的な使い方から実践的なクラス実装まで、具体例とともに丁寧に解説します。
stream_socket_get_name とは
| 項目 | 内容 |
|---|---|
| 関数名 | stream_socket_get_name |
| PHPバージョン | PHP 5.0.0以降 |
| カテゴリ | ストリーム関数 |
| 返り値 | string(アドレス文字列)、false(失敗時) |
構文
stream_socket_get_name(resource $socket, bool $remote): string|false
パラメータ
| パラメータ | 型 | 説明 |
|---|---|---|
$socket | resource | 対象のストリームソケット |
$remote | bool | true でリモート側、false でローカル側のアドレスを返す |
返り値
| 値 | 意味 |
|---|---|
string | "IPアドレス:ポート番号" の形式(例:"93.184.216.34:80") |
false | 失敗(ソケットが接続されていないなど) |
ローカルとリモートの違い
【クライアント側から見た場合】
自分(クライアント) サーバー
192.168.1.10:54321 ────────→ 93.184.216.34:80
stream_socket_get_name($sock, false) → "192.168.1.10:54321" ← ローカル
stream_socket_get_name($sock, true) → "93.184.216.34:80" ← リモート
【サーバー側から見た場合(accept後のクライアントストリーム)】
クライアント 自分(サーバー)
192.168.1.10:54321 ────────→ 0.0.0.0:8080
stream_socket_get_name($client, false) → "0.0.0.0:8080" ← ローカル
stream_socket_get_name($client, true) → "192.168.1.10:54321" ← リモート(接続元)
基本的な使い方
<?php
// サーバーに接続
$socket = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5.0);
if ($socket === false) {
die("接続失敗: [{$errno}] {$errstr}");
}
// ローカル側(自分)のアドレス
$localAddr = stream_socket_get_name($socket, false);
echo "ローカル: {$localAddr}" . PHP_EOL; // 例: 192.168.1.10:54321
// リモート側(サーバー)のアドレス
$remoteAddr = stream_socket_get_name($socket, true);
echo "リモート: {$remoteAddr}" . PHP_EOL; // 例: 93.184.216.34:80
fclose($socket);
実践例(クラスを使った実装)
例1:アドレス情報をパースして使いやすくする SocketAddress クラス
返り値の文字列を IP・ポート・ホスト名などに分解する汎用クラスです。
<?php
class SocketAddress
{
private string $raw;
private string $ip;
private int $port;
private function __construct(string $raw)
{
$this->raw = $raw;
[$this->ip, $portStr] = $this->parse($raw);
$this->port = (int) $portStr;
}
public static function fromSocket(resource $socket, bool $remote): ?self
{
$raw = stream_socket_get_name($socket, $remote);
return $raw !== false ? new self($raw) : null;
}
public static function fromString(string $address): self
{
return new self($address);
}
public function getIp(): string
{
return $this->ip;
}
public function getPort(): int
{
return $this->port;
}
public function getRaw(): string
{
return $this->raw;
}
public function isPrivate(): bool
{
// RFC1918 プライベートアドレス
return filter_var(
$this->ip,
FILTER_VALIDATE_IP,
FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
) === false;
}
public function isLoopback(): bool
{
return $this->ip === '127.0.0.1' || $this->ip === '::1';
}
public function isIPv6(): bool
{
return str_contains($this->ip, ':');
}
public function getHostname(): string
{
$host = gethostbyaddr($this->ip);
return $host !== false ? $host : $this->ip;
}
public function __toString(): string
{
return $this->raw;
}
private function parse(string $raw): array
{
// IPv6 の場合:[::1]:8080
if (str_starts_with($raw, '[')) {
preg_match('/^\[([^\]]+)\]:(\d+)$/', $raw, $m);
return [$m[1] ?? $raw, $m[2] ?? '0'];
}
// IPv4 の場合:192.168.1.1:8080
$pos = strrpos($raw, ':');
if ($pos !== false) {
return [substr($raw, 0, $pos), substr($raw, $pos + 1)];
}
return [$raw, '0'];
}
}
// 使用例
$socket = stream_socket_client('tcp://example.com:80', $errno, $errstr, 5.0);
if ($socket) {
$local = SocketAddress::fromSocket($socket, false);
$remote = SocketAddress::fromSocket($socket, true);
if ($local && $remote) {
echo "=== ローカル ===" . PHP_EOL;
echo " アドレス : {$local->getRaw()}" . PHP_EOL;
echo " IP : {$local->getIp()}" . PHP_EOL;
echo " ポート : {$local->getPort()}" . PHP_EOL;
echo " プライベート: " . ($local->isPrivate() ? 'はい' : 'いいえ') . PHP_EOL;
echo " ループバック: " . ($local->isLoopback() ? 'はい' : 'いいえ') . PHP_EOL;
echo PHP_EOL . "=== リモート ===" . PHP_EOL;
echo " アドレス : {$remote->getRaw()}" . PHP_EOL;
echo " IP : {$remote->getIp()}" . PHP_EOL;
echo " ポート : {$remote->getPort()}" . PHP_EOL;
echo " ホスト名 : {$remote->getHostname()}" . PHP_EOL;
}
fclose($socket);
}
出力例:
=== ローカル ===
アドレス : 192.168.1.10:54321
IP : 192.168.1.10
ポート : 54321
プライベート: はい
ループバック: いいえ
=== リモート ===
アドレス : 93.184.216.34:80
IP : 93.184.216.34
ポート : 80
ホスト名 : 93.184.216.34.in-addr.arpa
例2:サーバー側でIPベースのアクセス制御を実装する
stream_socket_accept と組み合わせて、接続元IPに基づいてアクセスを許可・拒否します。
<?php
class AccessControlServer
{
private $server;
private array $allowList;
private array $denyList;
private array $connectionLog = [];
public function __construct(
string $address,
array $allowList = [], // 空 = すべて許可
array $denyList = []
) {
$this->allowList = $allowList;
$this->denyList = $denyList;
$this->server = stream_socket_server($address, $errno, $errstr);
if (!$this->server) {
throw new RuntimeException("起動失敗 [{$errno}]: {$errstr}");
}
echo "アクセス制御サーバー起動: {$address}" . PHP_EOL;
}
public function acceptWithControl(float $timeout = 5.0): void
{
$client = stream_socket_accept($this->server, $timeout, $peerName);
if ($client === false) {
echo "タイムアウト" . PHP_EOL;
return;
}
// リモートアドレスを取得
$remoteRaw = stream_socket_get_name($client, true);
$localRaw = stream_socket_get_name($client, false);
$remoteIp = $remoteRaw !== false ? explode(':', $remoteRaw)[0] : 'unknown';
$allowed = $this->isAllowed($remoteIp);
$this->connectionLog[] = [
'time' => date('H:i:s'),
'remote' => $remoteRaw,
'local' => $localRaw,
'allowed' => $allowed,
];
if ($allowed) {
echo "[許可] {$remoteRaw} → {$localRaw}" . PHP_EOL;
fwrite($client, "200 接続を受け付けました({$remoteRaw})\r\n");
} else {
echo "[拒否] {$remoteRaw}" . PHP_EOL;
fwrite($client, "403 アクセスが拒否されました({$remoteIp})\r\n");
}
fclose($client);
}
private function isAllowed(string $ip): bool
{
// 拒否リストに含まれていれば拒否
foreach ($this->denyList as $denied) {
if ($this->matchCidr($ip, $denied)) {
return false;
}
}
// 許可リストが空ならすべて許可
if (empty($this->allowList)) {
return true;
}
// 許可リストに含まれていれば許可
foreach ($this->allowList as $allowed) {
if ($this->matchCidr($ip, $allowed)) {
return true;
}
}
return false;
}
private function matchCidr(string $ip, string $cidr): bool
{
// 単純なIP比較(CIDR非対応の簡易版)
return $ip === $cidr || str_starts_with($ip, rtrim($cidr, '*'));
}
public function printLog(): void
{
echo PHP_EOL . "=== 接続ログ ===" . PHP_EOL;
foreach ($this->connectionLog as $entry) {
$mark = $entry['allowed'] ? '✓' : '✗';
echo " [{$entry['time']}] {$mark} {$entry['remote']} → {$entry['local']}" . PHP_EOL;
}
}
public function close(): void
{
fclose($this->server);
}
}
// 使用例
$server = new AccessControlServer(
'tcp://127.0.0.1:18083',
allowList: ['127.0.0.1'], // ループバックのみ許可
denyList: []
);
// テストクライアントで接続
$c1 = stream_socket_client('tcp://127.0.0.1:18083', $e, $es, 1);
$server->acceptWithControl(0.5);
if ($c1) {
echo "応答: " . trim(fgets($c1)) . PHP_EOL;
fclose($c1);
}
$server->printLog();
$server->close();
出力例:
アクセス制御サーバー起動: tcp://127.0.0.1:18083
[許可] 127.0.0.1:54322 → 127.0.0.1:18083
応答: 200 接続を受け付けました(127.0.0.1:54322)
=== 接続ログ ===
[12:00:01] ✓ 127.0.0.1:54322 → 127.0.0.1:18083
例3:接続情報を詳細にログする接続トラッカー
複数クライアントの接続・通信・切断をソケットアドレスで追跡します。
<?php
class ConnectionTracker
{
private array $connections = [];
public function register(resource $socket): string
{
$local = stream_socket_get_name($socket, false);
$remote = stream_socket_get_name($socket, true);
$id = spl_object_id((object) $socket) . '_' . microtime(true);
$this->connections[$id] = [
'id' => $id,
'local' => $local ?: 'N/A',
'remote' => $remote ?: 'N/A',
'connected_at' => microtime(true),
'bytes_sent' => 0,
'bytes_recv' => 0,
'closed_at' => null,
'close_reason' => null,
];
echo "[登録] ID:{$id} | {$remote} → {$local}" . PHP_EOL;
return $id;
}
public function recordSend(string $id, int $bytes): void
{
if (isset($this->connections[$id])) {
$this->connections[$id]['bytes_sent'] += $bytes;
}
}
public function recordRecv(string $id, int $bytes): void
{
if (isset($this->connections[$id])) {
$this->connections[$id]['bytes_recv'] += $bytes;
}
}
public function close(string $id, string $reason = '正常切断'): void
{
if (isset($this->connections[$id])) {
$this->connections[$id]['closed_at'] = microtime(true);
$this->connections[$id]['close_reason'] = $reason;
$remote = $this->connections[$id]['remote'];
echo "[切断] ID:{$id} | {$remote} ({$reason})" . PHP_EOL;
}
}
public function getConnection(string $id): ?array
{
return $this->connections[$id] ?? null;
}
public function printReport(): void
{
echo PHP_EOL . "=== 接続トラッキングレポート ===" . PHP_EOL;
foreach ($this->connections as $conn) {
$duration = $conn['closed_at']
? round(($conn['closed_at'] - $conn['connected_at']) * 1000, 1) . 'ms'
: '接続中';
echo str_repeat('-', 50) . PHP_EOL;
echo " リモート : {$conn['remote']}" . PHP_EOL;
echo " ローカル : {$conn['local']}" . PHP_EOL;
echo " 送信 : {$conn['bytes_sent']} bytes" . PHP_EOL;
echo " 受信 : {$conn['bytes_recv']} bytes" . PHP_EOL;
echo " 接続時間 : {$duration}" . PHP_EOL;
if ($conn['close_reason']) {
echo " 切断理由 : {$conn['close_reason']}" . PHP_EOL;
}
}
$total = count($this->connections);
$closed = count(array_filter($this->connections, fn($c) => $c['closed_at'] !== null));
echo str_repeat('-', 50) . PHP_EOL;
echo "合計: {$total} 接続(切断済み: {$closed})" . PHP_EOL;
}
}
// 使用例
$server = stream_socket_server('tcp://127.0.0.1:18084', $errno, $errstr);
$tracker = new ConnectionTracker();
// テスト用クライアントを3本作成
$clients = [];
for ($i = 0; $i < 3; $i++) {
$c = stream_socket_client('tcp://127.0.0.1:18084', $e, $es, 1);
if ($c) $clients[] = $c;
}
// サーバー側で受け付けてトラッキング
foreach ($clients as $c) {
$accepted = stream_socket_accept($server, 0.5);
if ($accepted) {
$id = $tracker->register($accepted);
$msg = "Hello from server\n";
$bytes = fwrite($accepted, $msg);
$tracker->recordSend($id, $bytes ?: 0);
$tracker->close($id);
fclose($accepted);
}
}
foreach ($clients as $c) fclose($c);
fclose($server);
$tracker->printReport();
出力例:
[登録] ID:xxx | 127.0.0.1:54323 → 127.0.0.1:18084
[登録] ID:xxx | 127.0.0.1:54324 → 127.0.0.1:18084
[登録] ID:xxx | 127.0.0.1:54325 → 127.0.0.1:18084
[切断] ID:xxx | 127.0.0.1:54323 (正常切断)
[切断] ID:xxx | 127.0.0.1:54324 (正常切断)
[切断] ID:xxx | 127.0.0.1:54325 (正常切断)
=== 接続トラッキングレポート ===
--------------------------------------------------
リモート : 127.0.0.1:54323
ローカル : 127.0.0.1:18084
送信 : 18 bytes
受信 : 0 bytes
接続時間 : 0.8ms
切断理由 : 正常切断
...
合計: 3 接続(切断済み: 3)
例4:ローカルアドレスを確認して動的バインドポートを検出する
0 番ポートで接続するとOSが空きポートを自動割り当てます。stream_socket_get_name でその番号を取得できます。
<?php
class DynamicPortDetector
{
/**
* OSに空きポートを割り当てさせてそのポート番号を返す
*/
public static function getFreePort(string $bindAddress = '127.0.0.1'): int
{
// ポート0でバインドするとOSが空きポートを自動割り当て
$server = stream_socket_server("tcp://{$bindAddress}:0", $errno, $errstr);
if (!$server) {
throw new RuntimeException("サーバー起動失敗 [{$errno}]: {$errstr}");
}
// ローカルアドレスからポート番号を取得
$localAddr = stream_socket_get_name($server, false);
fclose($server);
if ($localAddr === false) {
throw new RuntimeException("アドレス取得失敗");
}
// "127.0.0.1:XXXXX" からポート番号を抽出
$port = (int) substr($localAddr, strrpos($localAddr, ':') + 1);
return $port;
}
/**
* クライアント接続時のエフェメラルポートを取得する
*/
public static function getEphemeralPort(string $host, int $port): array
{
$socket = stream_socket_client("tcp://{$host}:{$port}", $errno, $errstr, 5.0);
if ($socket === false) {
throw new RuntimeException("接続失敗 [{$errno}]: {$errstr}");
}
$local = stream_socket_get_name($socket, false);
$remote = stream_socket_get_name($socket, true);
fclose($socket);
$localPort = $local !== false
? (int) substr($local, strrpos($local, ':') + 1)
: 0;
return [
'local_address' => $local,
'local_port' => $localPort,
'remote_address' => $remote,
'is_ephemeral' => $localPort >= 49152, // IANA エフェメラルポート範囲
];
}
}
// 使用例1:空きポートを取得
$freePorts = [];
for ($i = 0; $i < 5; $i++) {
$freePorts[] = DynamicPortDetector::getFreePort();
}
echo "空きポート一覧: " . implode(', ', $freePorts) . PHP_EOL;
// 使用例2:クライアント接続時のエフェメラルポートを確認
echo PHP_EOL;
try {
$info = DynamicPortDetector::getEphemeralPort('example.com', 80);
echo "ローカルアドレス : {$info['local_address']}" . PHP_EOL;
echo "ローカルポート : {$info['local_port']}" . PHP_EOL;
echo "リモートアドレス : {$info['remote_address']}" . PHP_EOL;
echo "エフェメラルポート: " . ($info['is_ephemeral'] ? 'はい' : 'いいえ') . PHP_EOL;
} catch (RuntimeException $e) {
echo "エラー: " . $e->getMessage() . PHP_EOL;
}
出力例:
空きポート一覧: 52341, 52342, 52343, 52344, 52345
ローカルアドレス : 192.168.1.10:54899
ローカルポート : 54899
リモートアドレス : 93.184.216.34:80
エフェメラルポート: はい
例5:STARTTLSの前後でアドレス情報の継続性を確認する
stream_socket_enable_crypto 前後でもアドレス情報が変わらないことを検証するデモです。
<?php
class TlsAddressInspector
{
/**
* TCP → TLS昇格の各フェーズでアドレス情報を取得して記録する
*/
public function inspect(string $host, int $port = 443): array
{
$phases = [];
// フェーズ1:TCP接続直後
$socket = stream_socket_client(
"tcp://{$host}:{$port}",
$errno, $errstr, 5.0
);
if ($socket === false) {
throw new RuntimeException("接続失敗 [{$errno}]: {$errstr}");
}
$phases['TCP接続後'] = $this->snapshot($socket);
// フェーズ2:TLS有効化後
stream_context_set_option($socket, 'ssl', 'verify_peer', true);
stream_context_set_option($socket, 'ssl', 'verify_peer_name', true);
stream_context_set_option($socket, 'ssl', 'peer_name', $host);
$result = stream_socket_enable_crypto(
$socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT
);
if ($result === true) {
$phases['TLS有効化後'] = $this->snapshot($socket);
// フェーズ3:HTTP送信後
fwrite($socket, "GET / HTTP/1.0\r\nHost: {$host}\r\n\r\n");
$phases['HTTP送信後'] = $this->snapshot($socket);
}
fclose($socket);
return $phases;
}
private function snapshot(resource $socket): array
{
$local = stream_socket_get_name($socket, false);
$remote = stream_socket_get_name($socket, true);
$meta = stream_get_meta_data($socket);
return [
'local' => $local ?: 'N/A',
'remote' => $remote ?: 'N/A',
'blocking' => $meta['blocked'],
'tls' => isset($meta['crypto']) ? ($meta['crypto']['protocol'] ?? 'none') : 'none',
];
}
}
// 使用例
$inspector = new TlsAddressInspector();
try {
$phases = $inspector->inspect('www.example.com', 443);
echo str_pad("フェーズ", 16)
. str_pad("ローカル", 24)
. str_pad("リモート", 24)
. "TLS" . PHP_EOL;
echo str_repeat('-', 72) . PHP_EOL;
foreach ($phases as $phase => $info) {
echo str_pad($phase, 16)
. str_pad($info['local'], 24)
. str_pad($info['remote'], 24)
. $info['tls'] . PHP_EOL;
}
echo PHP_EOL . "※ TCP → TLS 昇格後もローカル・リモートアドレスは変化しません" . PHP_EOL;
} catch (RuntimeException $e) {
echo "エラー: " . $e->getMessage() . PHP_EOL;
}
出力例:
フェーズ ローカル リモート TLS
------------------------------------------------------------------------
TCP接続後 192.168.1.10:54901 93.184.216.34:443 none
TLS有効化後 192.168.1.10:54901 93.184.216.34:443 TLSv1.3
HTTP送信後 192.168.1.10:54901 93.184.216.34:443 TLSv1.3
※ TCP → TLS 昇格後もローカル・リモートアドレスは変化しません
例6:マルチクライアントサーバーで全接続情報を一覧表示する
stream_select と組み合わせ、全クライアントのアドレスをリアルタイムで管理します。
<?php
class ConnectionRegistry
{
private $server;
private array $clients = [];
private array $addresses = [];
public function __construct(string $address)
{
$this->server = stream_socket_server($address, $errno, $errstr);
if (!$this->server) {
throw new RuntimeException("起動失敗 [{$errno}]: {$errstr}");
}
stream_set_blocking($this->server, false);
}
public function tick(): void
{
$read = array_merge([$this->server], $this->clients);
$write = $except = null;
if (stream_select($read, $write, $except, 0, 50_000) === false) {
return;
}
foreach ($read as $sock) {
if ($sock === $this->server) {
$client = stream_socket_accept($this->server, 0);
if ($client) {
stream_set_blocking($client, false);
$id = (int) $client;
// 両方向のアドレスを取得して記録
$local = stream_socket_get_name($client, false) ?: 'N/A';
$remote = stream_socket_get_name($client, true) ?: 'N/A';
$this->clients[$id] = $client;
$this->addresses[$id] = [
'local' => $local,
'remote' => $remote,
'connected_at' => date('H:i:s'),
];
echo "[+] 接続: {$remote}" . PHP_EOL;
fwrite($client, "接続しました(あなたのアドレス: {$remote})\n");
}
} else {
$id = (int) $sock;
$data = fread($sock, 1024);
if ($data === false || $data === '' || feof($sock)) {
$addr = $this->addresses[$id]['remote'] ?? 'unknown';
echo "[-] 切断: {$addr}" . PHP_EOL;
fclose($sock);
unset($this->clients[$id], $this->addresses[$id]);
}
}
}
}
public function printTable(): void
{
echo PHP_EOL . "=== 接続一覧 ===" . PHP_EOL;
echo str_pad("リモート", 24)
. str_pad("ローカル", 24)
. "接続時刻" . PHP_EOL;
echo str_repeat('-', 58) . PHP_EOL;
if (empty($this->addresses)) {
echo " 接続なし" . PHP_EOL;
}
foreach ($this->addresses as $info) {
echo str_pad($info['remote'], 24)
. str_pad($info['local'], 24)
. $info['connected_at'] . PHP_EOL;
}
echo "合計: " . count($this->addresses) . " 接続" . PHP_EOL;
}
public function shutdown(): void
{
foreach ($this->clients as $c) fclose($c);
fclose($this->server);
}
}
// 使用例
$registry = new ConnectionRegistry('tcp://127.0.0.1:18085');
// テストクライアントを3本接続
$testClients = [];
for ($i = 0; $i < 3; $i++) {
$tc = stream_socket_client('tcp://127.0.0.1:18085', $e, $es, 1);
if ($tc) $testClients[] = $tc;
}
// 数回 tick して接続を処理
for ($i = 0; $i < 5; $i++) {
$registry->tick();
}
$registry->printTable();
foreach ($testClients as $tc) fclose($tc);
$registry->shutdown();
出力例:
[+] 接続: 127.0.0.1:54326
[+] 接続: 127.0.0.1:54327
[+] 接続: 127.0.0.1:54328
=== 接続一覧 ===
リモート ローカル 接続時刻
----------------------------------------------------------
127.0.0.1:54326 127.0.0.1:18085 12:00:01
127.0.0.1:54327 127.0.0.1:18085 12:00:01
127.0.0.1:54328 127.0.0.1:18085 12:00:01
合計: 3 接続
関連する関数との比較
| 関数 | 役割 |
|---|---|
stream_socket_get_name | ソケットのローカル/リモートアドレスを取得 |
stream_socket_client | ソケット接続を確立(クライアント側) |
stream_socket_server | リスニングソケットを作成(サーバー側) |
stream_socket_accept | 接続を受け付け($peer_name 引数でも取得可) |
stream_get_meta_data | ソケットのメタ情報全般を取得 |
gethostbyaddr | IPアドレスからホスト名を逆引き |
stream_socket_accept の $peer_name との違い
// stream_socket_accept の $peer_name でリモートアドレスを取得
$client = stream_socket_accept($server, 30, $peerName);
echo $peerName; // "192.168.1.10:54321"(accept時の1回のみ)
// stream_socket_get_name は accept 後いつでも取得可能
$remote = stream_socket_get_name($client, true);
$local = stream_socket_get_name($client, false);
| 観点 | $peer_name(accept引数) | stream_socket_get_name |
|---|---|---|
| 取得できる情報 | リモートのみ | ローカル・リモート両方 |
| 取得タイミング | accept時のみ | いつでも呼び出し可能 |
| 用途 | シンプルな接続元ログ | 詳細な接続情報管理 |
よくある注意点・落とし穴
1. 接続前・切断後は false を返す
ソケットが接続状態にない場合は false が返ります。必ずチェックしてください。
$socket = stream_socket_client('tcp://example.com:80', $e, $es, 5);
// 接続前はアドレスが取れない(stream_socket_client 直後は接続済みだが念のため確認)
$name = stream_socket_get_name($socket, true);
if ($name === false) {
echo "アドレス取得失敗";
}
2. UNIXドメインソケットはポートがない
unix:// スキームのソケットでは IP:ポート形式ではなく、ソケットファイルのパスが返ります。
$socket = stream_socket_client('unix:///var/run/app.sock');
$name = stream_socket_get_name($socket, false);
// → "/var/run/app.sock" (IPアドレスでなくパスが返る)
3. IPv6 アドレスは括弧付きで返る
IPv6 の場合、返り値は [::1]:8080 のような形式になります。パース時は注意が必要です。
$name = stream_socket_get_name($socket, true);
// IPv6 の例: "[::1]:8080"
// IPv6 対応のパース
if (str_starts_with($name, '[')) {
preg_match('/^\[([^\]]+)\]:(\d+)$/', $name, $m);
$ip = $m[1]; // "::1"
$port = $m[2]; // "8080"
}
4. リスニングソケット自体に $remote=true は使えない
stream_socket_server で作成したリスニングソケットに対してリモートアドレスを取得しようとすると false になります。
$server = stream_socket_server('tcp://0.0.0.0:8080');
echo stream_socket_get_name($server, false); // "0.0.0.0:8080"(OK)
echo stream_socket_get_name($server, true); // false(リモートは存在しない)
まとめ
| 項目 | 内容 |
|---|---|
| 関数名 | stream_socket_get_name(resource $socket, bool $remote): string|false |
$remote=false | ローカル側(自分)のアドレスを返す |
$remote=true | リモート側(相手)のアドレスを返す |
| 返り値形式 | "IPアドレス:ポート"(IPv6は "[::1]:port") |
| UNIXソケット | ソケットファイルのパスを返す |
| 注意点 | 未接続・切断後は false、リスニングソケットへの true は false |
| PHP バージョン | PHP 5.0.0 以上 |
stream_socket_get_name はシンプルながら、TCPサーバー・クライアントの実装において接続管理・アクセス制御・ログ記録など多くの場面で活躍する重要な関数です。stream_socket_accept の $peer_name では取れないローカルアドレスも取得できる点が大きな強みです。
IPv6対応のパース処理や、UNIXドメインソケットでの挙動の違いを押さえつつ、接続情報の可視化や制御に積極的に活用してみてください。
