ホスト、ポートに対してソケット
接続し、サーバとの間でデータを
送受信
サーバ
(IPv4/IPv6を問わず)任意のポートでソケット接続を待ち
受け、接続したクライアントとの
間でデータを送受信
複数のソケットを生成する
デュアルスタック対応
サーバ
プロセス IPv6
IPv4
サーバ プログラム 接続
IPv6
IPv4
87
クライアントプログラムの ポイント
フォールバック:接続できない場合に別の 接続先への接続に切替える動作
接続先アドレス情報をリストで取得し、順にたどる
Client
Web Server
www.example.jp DNS Server
www.example.jp IN AAAA 2001:db8:100::1 www.example.jp IN A 192.0.2.1
①名前解決問合せ www.example.jp ?
②AAAA応答 2001:db8:100::1 A応答 192.0.2.1
③HTTP通信(IPv6) 2001:db8:100::1
192.0.2.1 2001:db8:ffff::1
198.51.100.1
④HTTP通信(IPv4)
フォールバック
コーディングの留意点
関数、データ型はIPv4/IPv6両対応のものを使用
する
データ型:文字列型 関数:
get̲dns̲record() gethostbyaddr()
ライブラリ、フィルタを用いて入力値検証、変換
ライブラリ:Net̲IPv6
フィルタ:FILTER̲VALIDATE̲IP
gethostbyname()
は IPv6 非対応
89
Sample2 処理フロー
ユーザ入力値から
接続先アドレス(リスト)を 取得
接続先アドレス(リスト)に、
順にソケットを生成して接続
Sample2 コード解説
(1)
<?php
$IS_DEBUG = 0;
$host = filter_input(INPUT_GET, 'host');
$port = filter_input(INPUT_GET, 'port', FILTER_VALIDATE_INT);
if ($host && $port){
$addresses = array();
if ($host_addr = filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)){
$addresses[0]['domain'] = AF_INET6;
$addresses[0]['address'] = $host_addr;
} elseif ($host_addr = filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){
$addresses[0]['domain'] = AF_INET;
$addresses[0]['address'] = $host_addr;
■
フィルタを用いて変数が アドレスか判断91
Sample2 コード解説
(2)
} else {
$host_list = dns_get_record($host);
$size = sizeof($host_list);
for ($loopcnt = 0; $loopcnt < $size; $loopcnt++){
if ($host_list[$loopcnt]['type'] === 'AAAA'){
$addresses[$loopcnt]['domain'] = AF_INET6;
$addresses[$loopcnt]['address']
= $host_list[$loopcnt]['ipv6'];
} else {
$addresses[$loopcnt]['domain'] = AF_INET;
$addresses[$loopcnt]['address']
= $host_list[$loopcnt]['ip'];
} } }
$size = sizeof($addresses);
$message = "接続先ホスト名 " . $host . " ポート番号 " . $port . "<BR>\n";
ホスト名の場合には
DNS
から アドレスをリストで取得■
リストの数だけ、アドレスを取得し接続先候補とする■ IPv6
はAAAA
レコード、IPv4
はA
レコードに格納gethostbyname()は、
IPv6非対応
Sample2 コード解説
(3)
$connect_flag = 0;
for ($loopcnt = 0; $loopcnt < $size && $connect_flag === 0; $loopcnt++){
if (($socket = socket_create($addresses[$loopcnt]['domain'], SOCK_STREAM, SOL_TCP)) === FALSE){
$error_code = socket_last_error();
$error_msg = socket_strerror($error_code);
$message .= "connect to " . $addresses[$loopcnt]['address'] . "<BR>\n";
$message .= 'socket create error: [' . $error_code . '] ' . $error_msg . "<BR>\n";
} else {
$message .= 'socket connect (' . ($loopcnt +1) . ') : ' . $addresses[$loopcnt]
['address'] . " port: " . $port . "<BR>\n";
ソケット作る
93
Sample2 コード解説
(4)
if (socket_connect($socket, $addresses[$loopcnt]['address'], $port)){
$connect_flag = 1;
$response = socket_read($socket, 1024);
$message .= "サーバからのメッセージ:" . '<div style="margin: 10px">' .
$response . '</div>' . "<BR>\n";
} else {
$error_code = socket_last_error();
$error_msg = socket_strerror($error_code);
$message .= 'socket connect error: [' . $error_code . '] ' . $error_msg .
"<BR>\n";
}
socket_close($socket);
} }
} else {
$message = "接続先ホスト名 " . $host . " もしくはポート番号 " . $port . "が入力さ れていません";
}
?>
接続する
切断する
Sample2 コード解説
(5)
<html>
<head>
<meta charset="UTF-8">
<title>Socket通信クライアント(デュアルスタック版)</title>
</head>
<body>
<H1>Socket通信クライアント(デュアルスタック版)</H1>
<form action="<?php echo filter_input(INPUT_SERVER, 'PHP_SELF', FILTER_SANITIZE_URL)?>" method="GET">
接続先ホスト <input type='text' name='host' value='<?php echo $host; ?>'>
ポート番号 <input type='text' name='port'value='<?php echo $port; ?>'>
<input type="submit" value="実行する">
</form>
<HR>
4. Apple の IPv6 対応解説
95
今年5月の
Apple
のアナウンス
2016/6/1 から、App Store に載せるアプリは、
IPv6-only
ネットワークで動 作しないといけないほとんどのアプリは何も
変更しなくて大丈夫なはず
もし、IPv4 固有の API や IPア ドレスをハードコードしていた ら、(