ホームページ >バックエンド開発 >PHPチュートリアル >PHPでのソケット通信の詳細説明
場合によっては、PHP プログラムが他のシステムと通信する必要がある場合があります。たとえば、企業の公式 Web サイトでは、製品のトレーサビリティ情報の照会が行われ、その際、PHP ネットワーク プログラミングが必要になります。 PHP はソケット拡張機能を提供します。公式 Web サイトのアドレスは次のとおりです:
http://nl3.php.net/manual/zh/intro.sockets.php
この拡張機能により、ソケットを直接操作できるようになります。 php 経由で他のシステムと通信できるようにソケットに接続します。ソケットを使用して OSI ネットワーク モデルのトランスポート層の上で動作し、TCP および UDP によって提供されるサービスを直接使用します。そのため、他のシステムのクライアントとして使用できます。 HTTP クライアント (ブラウザ)、一般的な smtp、pop、ftp などのアプリケーション層プロトコルをシミュレートできます。さらに興味深いのは、これらのプロトコルはすべてアプリケーション層です。したがって、システム間の通信にはカスタム プロトコルが必要です。
ここでは、PHP を使用して単純なファイル受信サーバーを開発する例を示します。別の PHP クライアント プログラムは、このサーバー プログラムと通信する方法を示しています。コードは次のとおりです。
サーバー側プログラム:
<?php /** * 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展 * 这是一个简易的服务器程序,接收客户端发送的文件,保存后关闭 * 通讯协议约定为:文件大小::文件扩展名::有效数据 * 通讯结束标识符为:“-end-” * 在命令行或浏览器中执行均可,推荐命令行 * 作者:云客【云游天下,作客四方】 */ /****配置****/ //要绑定监听的本机ip地址和端口 $address = '127.0.0.1'; $port = 81; error_reporting(E_ALL); //防止超时 set_time_limit(0); //开启绝对刷送,禁止缓冲内容 ob_implicit_flush(); //创建套接字资源 if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) { echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n"; } //绑定套接字到端口 if (socket_bind($sock, $address, $port) === false) { echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; } //开始监听端口,参数5表示可以让5个连接请求在缓冲中排队 //排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将直接忽略,客户端显示无法链接 //注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队 if (socket_listen($sock, 5) === false) { echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; } do { //程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源 if (($client_sock = socket_accept($sock)) === false) { echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; break; } echo "[client connect start]\n"; $data = ""; //接收的数据 $data_size = -1; //接收的数据大小 $received_size = 0; //实际接收的数据 $ext_name = "txt"; //默认文件扩展名 //开始与客户端交互 do { //运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容 if (false === ($buf = socket_read($client_sock, 2048))) { echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($client_sock)) . "\n"; break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器 } if ($data_size === -1) {//第一次接受数据,解析通讯协议 $arr = explode("::", $buf, 3); $data_size = (int)$arr[0]; $ext_name = $arr[1]; $data .= $arr[2]; $received_size += strlen($data); continue; } $data .= $buf; $received_size += strlen($buf); if ($data_size <= $received_size) { if ($data_size < $received_size) { $data = substr($data, 0, $data_size); } echo "received:" . $received_size . "/" . "total:" . $data_size; $talkback = "-end-"; //发送通讯结束标志 socket_write($client_sock, $talkback, strlen($talkback)); file_put_contents("servertest." . $ext_name, $data); break 2; //关闭服务器 } echo $talkback = "received:" . $received_size . "\n"; socket_write($client_sock, $talkback, strlen($talkback)); } while (true); socket_close($client_sock); } while (true); socket_close($sock); ?>
クライアント側プログラム:
<?php /** * 本程序演示php网络编程:socket通讯 需要php开启php_sockets扩展 * 这是一个简易的客户端程序,向服务器发送文件 * 通讯协议约定为:文件大小::文件扩展名::有效数据 * 通讯结束标识符为:“-end-” * 在命令行或浏览器中执行均可,推荐命令行,需先开启服务器端 * 作者:云客【云游天下,作客四方】 */ /****配置****/ $file = "yunke.jpg"; //待发送的文件 //服务器ip和端口号 $address = '127.0.0.1'; $port = 81; error_reporting(E_ALL); // 防止超时 set_time_limit(0); // 开启绝对刷送,不要缓冲输出 ob_implicit_flush(true); //创建套接字资源 if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) { echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n"; exit(); } //打开到远程主机的链接 if (socket_connect($sock, $address, $port) === false) { echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; exit(); } //构造一个很大的待发送数据 大约10MB /* $data = ""; for ($i = 1; $i <= 10000000; $i++) { $data .= "data:" . $i; } $data = $data . $data . $data; */ $data = file_get_contents($file); $data_size = strlen($data); $arr_temp = explode('.', $file); $ext_name = end($arr_temp); $sock_data = $data_size . "::" . $ext_name . "::" . $data; //发送数据给远程主机 while (true) { $sock_data_size = strlen($sock_data); $send_size = socket_write($sock, $sock_data, $sock_data_size); if ($send_size === false) { echo "send false:" . socket_strerror(socket_last_error($sock)) . "\n"; socket_close($sock); echo "[client shutdown]\n"; exit(); } if ($send_size == $sock_data_size) { break; } $sock_data = substr($sock_data, $send_size); } //读返回数据 while (true) { if (false === ($out = socket_read($sock, 2048))) { echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; break 1; } echo "server: " . $out . "\n"; if (substr($out, -5) == "-end-") { //约定结束标志 break; } } //关闭套接字资源 socket_close($sock); echo "[client shutdown]\nsend data:" . format($data_size); function format($byte = 0) { if ($byte > 1024 * 1024) { return ceil($byte / (1024 * 1024)) . "MB"; } elseif ($byte > 1024) { return ceil($byte / 1024) . "KB"; } else { return $byte . " Byte"; } }
この例は、実際のプロジェクトで通信するために、TCP ショート リンクを介してサーバーにファイルを送信する方法を示しています。モジュールはそれほど単純ではなく、さらに多くの問題を考慮する必要があります
例:
長い接続方法が必要かどうか (データは TCP リンクで複数回送受信される)、データ検証損傷の防止、および異なるシステム間のサイズ エンディアンネスの問題、カスタム通信プロトコル、パケット固着の問題、タイムアウト処理、同時アクセス、フロー制御、TCP パケットのアンパックなど
PHP ネットワークを深く理解したい場合プログラミングについては、上記のすべてを体系的に学習する必要があります。workman の実装を参照することをお勧めします
これは、ソケット通信の問題を解決するために使用できる、PHP で書かれたソケット サーバー フレームワークです。カスタムサーバーなど
workerman 公式 Web サイトのアドレスは次のとおりです: http://www.workerman.net/
以下は、サーバーの HTTP ヘッダーとブラウザーのヘッダーを表示するためのさらに 2 つのサンプル プログラムです:
次の例では、サーバーから返されたヘッダー情報を表示し、表示する必要があるサーバー アドレスを変更して、ブラウザーでスクリプトにアクセスするだけです。
<?php error_reporting(E_ALL); // 防止超时 set_time_limit(0); // 开启绝对刷送,不要缓冲输出 ob_implicit_flush(true); $address = 'www.qq.com'; //要查看的服务器 $port=80; //创建套接字资源 if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) { echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n"; exit(); } //打开到远程主机的链接 if (socket_connect($sock, gethostbyname($address), $port) === false) { echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; exit(); } //构造http头 $msg = "GET / HTTP/1.1 \r\n"; $msg .= "Host: {$address}\r\n"; $msg .= "Connection: Close\r\n\r\n"; //指示服务器发回完毕就断开,不必等等其他资源的链接 //如果没有该行会,服务器会一直等待我方继续发送数据,直到超时关闭 //而我方不会再发送,这个会让下面的socket_read函数互为等待,等很久 //发送给远程主机 socket_write($sock, $msg, strlen($msg)); $str=""; while ($out = @socket_read($sock, 2048*4)) { $str.=$out; } $str=explode("\r\n\r\n", $str); $str=$str[0]; if($str) { echo "<pre class="brush:php;toolbar:false">\r\n".$str."\r\n"; }else{ echo "nothing"; } //关闭套接字资源 socket_close($sock);
以下は、ブラウザのヘッダーを使用すると、ブラウザから送信されたセッション情報を簡単に表示できます
まずポート 80 が閉じられていることを確認してから、マシンのホスト ファイルを変更し、アクセスしたい URL をローカル 127.0.0.1 アドレスに誘導し、php を使用します。コマンド ライン モードでこのスクリプトを開始し、ブラウザを使用して設定された URL にアクセスします。プログラムを閉じる必要がある場合は、コンソールで Ctrl+C キーの組み合わせを使用してください。は次のとおりです:
<?php error_reporting(E_ALL); //防止超时 set_time_limit(0); //开启绝对刷送,禁止缓冲内容 ob_implicit_flush(); $address = '127.0.0.1'; $port = 80; //创建套接字资源 if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) { echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n"; } //绑定套接字到端口 if (socket_bind($sock, $address, $port) === false) { echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; } //开始监听端口,参数5表示可以让5个连接请求在缓冲中排队 //排队的链接请求会在前一个连接断掉后才开始执行,该处缓冲排队数满五个后,后面的链接请求将显示无法链接 //注意这个5并不是指可以并发进行5个链接,而是允许让5个后续链接进入排队 if (socket_listen($sock, 5) === false) { echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; } do { //程序运行到此处进行阻塞,就像暂停执行一样,一旦有请求进入,该函数停止阻塞,返回链接资源 //可以使用socket_set_nonblock函数设置非阻塞模式 if (($msgsock = socket_accept($sock)) === false) { echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($sock)) . "\n"; break; } //链接成功后在控制台显示提示 echo "[client start ".date("Y-m-d H:i:s")."]\n\n"; do { //运行到该处产生阻塞,一旦有内容则停止阻塞状态,返回内容 if (false === ($buf = socket_read($msgsock, 2048*6))) { echo "socket_read() failed: reason: " . socket_strerror(socket_last_error($msgsock)) . "\n"; break 2; //读取不了就返回失败,此处的2会让客户端断掉链接后停止本模拟服务器 } $talkback ="HTTP/1.1 200 OK\r\n\r\n"; $talkback .= date("Y-m-d H:i:s")."\nBrowser HTTP Headers:\n\n".$buf."\n"; //向客户端显示该内容 socket_write($msgsock, $talkback, strlen($talkback)); //向服务器控制台显示该内容 echo "$buf\n"; break ; //中断本次与浏览器的链接 } while (true); socket_close($msgsock); } while (true); socket_close($sock); ?>
関連する推奨事項:
Linux でソケット通信用のローカル ソース ポート番号を取得する方法
以上がPHPでのソケット通信の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。