問題描述
我正在尋找一個簡單的代碼來創建一個 WebSocket 服務器.我找到了 phpwebsockets 但它現在已經過時并且不支持最新的協議.我嘗試自己更新它,但它似乎不起作用.
I am looking for a simple code to create a WebSocket server. I found phpwebsockets but it is outdated now and doesn't support the newest protocol. I tried updating it myself but it doesn't seem to work.
#!/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')."
";
echo "Master socket : ".$master."
";
echo "Listening on : ".$address." port ".$port."
";
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("
Requesting 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
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: $acceptkey
";
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: (.*)
/" ,$req,$match)){ $h=$match[1]; }
if(preg_match("/Upgrade: (.*)
/",$req,$match)){ $u=$match[1]; }
if(preg_match("/Connection: (.*)
/",$req,$match)){ $c=$match[1]; }
if(preg_match("/Sec-WebSocket-Key: (.*)
/",$req,$match)){ $key=$match[1]; }
if(preg_match("/Sec-WebSocket-Protocol: (.*)
/",$req,$match)){ $protocol=$match[1]; }
if(preg_match("/Sec-WebSocket-Version: (.*)
/",$req,$match)){ $version=$match[1]; }
if(preg_match("/Origin: (.*)
/",$req,$match)){ $o=$match[1]; }
if(preg_match("/
(.*?)$/",$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."
"; }
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."
"; } }
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 說
If there is anything wrong in my code can you help me fix it? Concole in firefox says
Firefox 無法與位于 ws://localhost:12345/的服務器建立連接.
Firefox can't establish a connection to the server at ws://localhost:12345/.
推薦答案
我最近和你在同一條船上,這是我所做的:
I was in the same boat as you recently, and here is what I did:
我使用 phpwebsockets 代碼作為如何構建服務器端代碼的參考.(您似乎已經在這樣做了,而且正如您所指出的,由于各種原因,該代碼實際上不起作用.)
I used the phpwebsockets code as a reference for how to structure the server-side code. (You seem to already be doing this, and as you noted, the code doesn't actually work for a variety of reasons.)
我使用 PHP.net 閱讀了有關 每個套接字函數的詳細信息 在 phpwebsockets 代碼中使用.通過這樣做,我終于能夠從概念上理解整個系統是如何工作的.這是一個相當大的障礙.
I used PHP.net to read the details about every socket function used in the phpwebsockets code. By doing this, I was finally able to understand how the whole system works conceptually. This was a pretty big hurdle.
我閱讀了實際的 WebSocket 草案.我不得不多次閱讀這件事,最后才開始深入了解它.在整個過程中,您可能必須一次又一次地返回此文檔,因為它是具有正確、最新的權威資源有關 WebSocket API 的信息.
I read the actual WebSocket draft. I had to read this thing a bunch of times before it finally started to sink in. You will likely have to go back to this document again and again throughout the process, as it is the one definitive resource with correct, up-to-date information about the WebSocket API.
我根據 #3 中的草案中的說明編寫了正確的握手程序.這還不錯.
I coded the proper handshake procedure based on the instructions in the draft in #3. This wasn't too bad.
握手后,我不斷收到從客戶端發送到服務器的一堆亂碼,直到我意識到數據已編碼并且必須取消屏蔽后,我才弄清楚原因.以下鏈接在這里對我幫助很大:(original link 損壞) 存檔副本.
I kept getting a bunch of garbled text sent from the clients to the server after the handshake and I couldn't figure out why until I realized that the data is encoded and must be unmasked. The following link helped me a lot here: (original link broken) Archived copy.
請注意,此鏈接提供的代碼存在許多問題,如果不進一步修改將無法正常工作.
Please note that the code available at this link has a number of problems and won't work properly without further modification.
然后我遇到了以下 SO 線程,它清楚地解釋了如何正確編碼和解碼來回發送的消息:如何在服務器端發送和接收WebSocket消息?
I then came across the following SO thread, which clearly explains how to properly encode and decode messages being sent back and forth: How can I send and receive WebSocket messages on the server side?
這個鏈接真的很有幫助.我建議在查看 WebSocket 草案時咨詢它.這將有助于使草案的內容更有意義.
This link was really helpful. I recommend consulting it while looking at the WebSocket draft. It'll help make more sense out of what the draft is saying.
此時我差不多完成了,但是我使用 WebSocket 制作的 WebRTC 應用程序遇到了一些問題,所以我最終在 SO 上提出了我自己的問題,我最終解決了這個問題:WebRTC 候選信息末尾的數據是什么?
I was almost done at this point, but had some issues with a WebRTC app I was making using WebSocket, so I ended up asking my own question on SO, which I eventually solved: What is this data at the end of WebRTC candidate info?
在這一點上,我幾乎已經完成了所有工作.我只需要添加一些額外的邏輯來處理連接的關閉,就大功告成了.
At this point, I pretty much had it all working. I just had to add some additional logic for handling the closing of connections, and I was done.
這個過程總共花了我大約兩周的時間.好消息是,我現在非常了解 WebSocket,并且能夠從頭開始制作自己的客戶端和服務器腳本,效果很好.希望所有這些信息的總結將為您提供足夠的指導和信息來編寫您自己的 WebSocket PHP 腳本.
That process took me about two weeks total. The good news is that I understand WebSocket really well now and I was able to make my own client and server scripts from scratch that work great. Hopefully the culmination of all that information will give you enough guidance and information to code your own WebSocket PHP script.
祝你好運!
編輯:這個編輯是在我最初的答案之后幾年的,雖然我仍然有一個可行的解決方案,但它并沒有真正準備好分享.幸運的是,GitHub 上的其他人擁有與我幾乎相同的代碼(但更清晰),因此我建議將以下代碼用于有效的 PHP WebSocket 解決方案:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php
Edit: This edit is a couple of years after my original answer, and while I do still have a working solution, it's not really ready for sharing. Luckily, someone else on GitHub has almost identical code to mine (but much cleaner), so I recommend using the following code for a working PHP WebSocket solution:
https://github.com/ghedipunk/PHP-Websockets/blob/master/websockets.php
編輯 #2:雖然我仍然喜歡使用 PHP 來處理許多與服務器端相關的事情,但我必須承認,我最近對 ??Node.js 非常感興趣,并且主要原因是因為與 PHP(或任何其他服務器端語言)相比,它從一開始就更好地設計來處理 WebSocket.因此,我最近發現在您的服務器上設置 Apache/PHP 和 Node.js 并使用 Node.js 運行 WebSocket 服務器和使用 Apache/PHP 運行其他所有內容要容易得多.如果您處于無法為 WebSocket 安裝/使用 Node.js 的共享托管環境中,您可以使用免費服務,例如 Heroku 設置一個 Node.js WebSocket 服務器并從您的服務器向它發出跨域請求.只要確保您這樣做是為了將 WebSocket 服務器設置為能夠處理跨域請求.
Edit #2: While I still enjoy using PHP for a lot of server-side related things, I have to admit that I've really warmed up to Node.js a lot recently, and the main reason is because it's better designed from the ground up to handle WebSocket than PHP (or any other server-side language). As such, I've found recently that it's a lot easier to set up both Apache/PHP and Node.js on your server and use Node.js for running the WebSocket server and Apache/PHP for everything else. And in the case where you're on a shared hosting environment in which you can't install/use Node.js for WebSocket, you can use a free service like Heroku to set up a Node.js WebSocket server and make cross-domain requests to it from your server. Just make sure if you do that to set your WebSocket server up to be able to handle cross-origin requests.
這篇關于如何在 PHP 中創建 websockets 服務器的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!