[解決済み] PHPでwebsocketサーバーを作る方法
質問
私は、WebSocketサーバーを作成するための簡単なコードを探しています。私はphpwebsocketsを見つけましたが、それは今古いものであり、最新のプロトコルをサポートしていません。自分で更新してみましたが、うまくいかないようです。
#!/php -q
<?php /* >php -q server.php */
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$master = WebSocket("localhost",12345);
$sockets = array($master);
$users = array();
$debug = false;
while(true){
$changed = $sockets;
socket_select($changed,$write=NULL,$except=NULL,NULL);
foreach($changed as $socket){
if($socket==$master){
$client=socket_accept($master);
if($client<0){ console("socket_accept() failed"); continue; }
else{ connect($client); }
}
else{
$bytes = @socket_recv($socket,$buffer,2048,0);
if($bytes==0){ disconnect($socket); }
else{
$user = getuserbysocket($socket);
if(!$user->handshake){ dohandshake($user,$buffer); }
else{ process($user,$buffer); }
}
}
}
}
//---------------------------------------------------------------
function process($user,$msg){
$action = unwrap($msg);
say("< ".$action);
switch($action){
case "hello" : send($user->socket,"hello human"); break;
case "hi" : send($user->socket,"zup human"); break;
case "name" : send($user->socket,"my name is Multivac, silly I know"); break;
case "age" : send($user->socket,"I am older than time itself"); break;
case "date" : send($user->socket,"today is ".date("Y.m.d")); break;
case "time" : send($user->socket,"server time is ".date("H:i:s")); break;
case "thanks": send($user->socket,"you're welcome"); break;
case "bye" : send($user->socket,"bye"); break;
default : send($user->socket,$action." not understood"); break;
}
}
function send($client,$msg){
say("> ".$msg);
$msg = wrap($msg);
socket_write($client,$msg,strlen($msg));
}
function WebSocket($address,$port){
$master=socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die("socket_create() failed");
socket_set_option($master, SOL_SOCKET, SO_REUSEADDR, 1) or die("socket_option() failed");
socket_bind($master, $address, $port) or die("socket_bind() failed");
socket_listen($master,20) or die("socket_listen() failed");
echo "Server Started : ".date('Y-m-d H:i:s')."\n";
echo "Master socket : ".$master."\n";
echo "Listening on : ".$address." port ".$port."\n\n";
return $master;
}
function connect($socket){
global $sockets,$users;
$user = new User();
$user->id = uniqid();
$user->socket = $socket;
array_push($users,$user);
array_push($sockets,$socket);
console($socket." CONNECTED!");
}
function disconnect($socket){
global $sockets,$users;
$found=null;
$n=count($users);
for($i=0;$i<$n;$i++){
if($users[$i]->socket==$socket){ $found=$i; break; }
}
if(!is_null($found)){ array_splice($users,$found,1); }
$index = array_search($socket,$sockets);
socket_close($socket);
console($socket." DISCONNECTED!");
if($index>=0){ array_splice($sockets,$index,1); }
}
function dohandshake($user,$buffer){
console("\nRequesting handshake...");
console($buffer);
//list($resource,$host,$origin,$strkey1,$strkey2,$data)
list($resource,$host,$u,$c,$key,$protocol,$version,$origin,$data) = getheaders($buffer);
console("Handshaking...");
$acceptkey = base64_encode(sha1($key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$upgrade = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $acceptkey\r\n";
socket_write($user->socket,$upgrade,strlen($upgrade));
$user->handshake=true;
console($upgrade);
console("Done handshaking...");
return true;
}
function getheaders($req){
$r=$h=$u=$c=$key=$protocol=$version=$o=$data=null;
if(preg_match("/GET (.*) HTTP/" ,$req,$match)){ $r=$match[1]; }
if(preg_match("/Host: (.*)\r\n/" ,$req,$match)){ $h=$match[1]; }
if(preg_match("/Upgrade: (.*)\r\n/",$req,$match)){ $u=$match[1]; }
if(preg_match("/Connection: (.*)\r\n/",$req,$match)){ $c=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)\r\n/",$req,$match)){ $key=$match[1]; }
if(preg_match("/Sec-WebSocket-Protocol: (.*)\r\n/",$req,$match)){ $protocol=$match[1]; }
if(preg_match("/Sec-WebSocket-Version: (.*)\r\n/",$req,$match)){ $version=$match[1]; }
if(preg_match("/Origin: (.*)\r\n/",$req,$match)){ $o=$match[1]; }
if(preg_match("/\r\n(.*?)\$/",$req,$match)){ $data=$match[1]; }
return array($r,$h,$u,$c,$key,$protocol,$version,$o,$data);
}
function getuserbysocket($socket){
global $users;
$found=null;
foreach($users as $user){
if($user->socket==$socket){ $found=$user; break; }
}
return $found;
}
function say($msg=""){ echo $msg."\n"; }
function wrap($msg=""){ return chr(0).$msg.chr(255); }
function unwrap($msg=""){ return substr($msg,1,strlen($msg)-2); }
function console($msg=""){ global $debug; if($debug){ echo $msg."\n"; } }
class User{
var $id;
var $socket;
var $handshake;
}
?>
とクライアントがあります。
var connection = new WebSocket('ws://localhost:12345');
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
もし、私のコードに何か問題があれば、それを修正するのを手伝ってくれますか?FirefoxのConcoleは言う
<ブロッククオートFirefox が ws://localhost:12345/ にあるサーバーへの接続を確立できません。
解決方法は?
私も最近、あなたと同じ状況に陥りましたが、私が行ったことは次のとおりです。
-
を使いました。 phpwebsockets のコードは、サーバーサイドのコードをどのように構成するかのリファレンスとして使用しました。(すでにやっているようですが、ご指摘の通り、このコードは様々な理由で実際には機能しません)。
-
について、PHP.netを利用して詳細を読んでみました。 各ソケット機能 phpwebsocketsのコードで使用されています。そうすることで、ようやくシステム全体の仕組みが概念的に理解できるようになったのです。これはかなり大きなハードルだった。
-
を実際に読んでみました。 WebSocketのドラフト . 何度も読み返して、ようやく理解できるようになりました。この文書は、WebSocket API に関する正しく最新の情報を提供する唯一の決定的なリソースであるため、このプロセスを通じて何度も読み返す必要があります。
-
3.の草稿にある手順を元に、適切なハンドシェイクの手順をコーディングしました。これはあまり悪くなかったです。
-
ハンドシェイクの後、クライアントからサーバーに送られる文字化けしたテキストが何度も表示されましたが、データがエンコードされており、マスクを解除しなければならないことに気付くまで、その原因が分かりませんでした。ここでは、次のリンクがとても役に立ちました。(
オリジナル リンク ブロークン) アーカイブコピー .このリンク先で公開されているコードには多くの問題があり、さらに修正を加えないと正常に動作しませんので、ご注意ください。
-
そこで、以下のSOスレッドに出会い、メッセージの正しいエンコードとデコードの方法を明確に説明することができました。 サーバー側でWebSocketメッセージを送受信するにはどうすればよいですか?
このリンクは本当に役に立ちました。WebSocketのドラフトを見ながら、このリンクを参照することをお勧めします。草案が言っていることをより理解するのに役立つはずです。
-
この時点でほぼ終わっていたのですが、WebSocketを使って作っていたWebRTCアプリで問題が発生し、結局SOで自分で質問して解決しました。 WebRTCの候補情報の最後にあるこのデータは何ですか?
-
この時点で、私はほとんどすべてを動作させることができました。あとは接続の終了を処理するためのロジックを追加すれば完了です。
その作業には合計で2週間ほどかかりました。良いニュースとしては、私は今WebSocketを本当によく理解しており、素晴らしい動作をする独自のクライアントおよびサーバースクリプトをゼロから作ることができました。 これらの情報の集大成が、あなた自身のWebSocket PHPスクリプトをコーディングするのに十分な指針と情報を与えてくれることを願っています。
がんばってください
編集
: この編集は、私の最初の回答から数年後のもので、まだ動作するソリューションを持っていますが、共有するにはまだ早いです。幸運なことに、GitHub にいる他の人が私とほとんど同じコード (ただし、よりクリーンなもの) を持っているので、PHP WebSocket ソリューションを動作させるには以下のコードを使用することをおすすめします。
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php
2番を編集 : その主な理由は、Node.jsがPHP(または他のサーバーサイド言語)よりもWebSocketを処理するために基礎から設計されているからです。そのため、Apache/PHPとNode.jsの両方をサーバーにセットアップして、WebSocketサーバーの実行にNode.jsを使用し、それ以外はApache/PHPを使用すると、かなり楽になることが最近わかってきた。また、WebSocket用のNode.jsをインストール/使用できない共用ホスティング環境の場合は、以下のような無料サービスを利用することができます。 ヘロク でNode.jsのWebSocketサーバを立ち上げ、自分のサーバからそのサーバにクロスドメインリクエストを行うことができます。この場合、クロスオリジンリクエストを処理できるように WebSocket サーバーをセットアップすることを確認してください。
関連
-
[解決済み】In_arrayが動作しない。
-
[解決済み] $_SERVER['DOCUMENT_ROOT'] と $_SERVER['HTTP_HOST'] の違いについて
-
[解決済み] 配列から特定の項目を削除するにはどうすればよいですか?
-
[解決済み] jQueryで要素が非表示になっているかどうかを確認するには?
-
[解決済み] JavaScriptで文字列が部分文字列を含むかどうかを確認する方法は?
-
[解決済み] あるJavaScriptファイルを他のJavaScriptファイルにインクルードするにはどうすればよいですか?
-
[解決済み] GUID / UUIDの作成方法
-
[解決済み】別のウェブページにリダイレクトするにはどうすればいいですか?
-
[解決済み】オブジェクトからプロパティを削除する(JavaScript)
-
[解決済み] リファレンス - このシンボルはPHPで何を意味するのですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】PHP - 構文エラー、予期しないT_CONSTANT_ECAPSED_STRING [閉店].
-
[解決済み】Fatal error: 未定義の関数 sqlsrv_connect() を呼び出した。
-
[解決済み】move_uploaded_fileは、「failed to open stream: Permission denied" というエラーが出る
-
[解決済み】PHP定数「PHP_EOL」はいつ使うの?
-
[解決済み】XAMPPエラー: www.example.com:443:0 サーバー証明書に、サーバー名と一致するIDが含まれていません。
-
[解決済み】Xampp ローカルホスト/ダッシュボード
-
[解決済み】PDOException SQLSTATE[HY000] [2002] そのようなファイルまたはディレクトリがありません。
-
[解決済み】PHPのクラスが見つからないが、インクルードされている
-
[解決済み] PHP - ストリームを開くのに失敗しました : そのようなファイルまたはディレクトリがありません。
-
[解決済み】書き込みコンテキストでメソッドの戻り値を使用することができない