PHPSocket.IO是PHP版本的Socket.IO服務(wù)端實(shí)現(xiàn),基于workerman開發(fā),用于替換node.js版本Socket.IO服務(wù)端。PHPSocket.IO底層采用websocket協(xié)議通訊,如果客戶端不支持websocket協(xié)議, 則會(huì)自動(dòng)采用http長輪詢的方式通訊。PHPSocket.IO實(shí)現(xiàn)的Polling通信機(jī)制包括Adobe Flash Socket、AJAX長輪詢、JSONP輪詢等。具體采用哪種機(jī)制通訊對(duì)于開發(fā)者完全透明, 開發(fā)者使用的是統(tǒng)一的接口。
PHPSocket.IO設(shè)計(jì)的目標(biāo)是利用PHP構(gòu)建能夠在不同瀏覽器和移動(dòng)設(shè)備上良好運(yùn)行的實(shí)時(shí)應(yīng)用,如實(shí)時(shí)分析系統(tǒng)、在線聊天室、在線客服系統(tǒng)、評(píng)論系統(tǒng)、WebIM等。 PHPSocket.IO與workerman的區(qū)別是,PHPSocket.IO基于workerman開發(fā),workerman有的特性PHPSocket.IO都支持。 PHPSocket.IO最大的優(yōu)勢(shì)是對(duì)各種瀏覽器的兼容性更好。
https://github.com/walkor/phpsocket.io
請(qǐng)使用composer集成phpsocket.io。
composer require workerman/phpsocket.io
腳本中引用vendor中的autoload.php實(shí)現(xiàn)SocketIO相關(guān)類的加載。例如
require_once '/你的vendor路徑/autoload.php';
創(chuàng)建一個(gè)SocketIO服務(wù)端
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use PHPSocketIO\SocketIO;
// 創(chuàng)建socket.io服務(wù)端,監(jiān)聽3120端口
$io = new SocketIO(3120);
// 當(dāng)有客戶端連接時(shí)打印一行文字
$io->on('connection', function($socket)use($io){
echo "new connection coming\n";
});
Worker::runAll();
客戶端
<script src='https://cdn.bootcss.com/socket.io/2.0.3/socket.io.js'></script>
<script>
// 如果服務(wù)端不在本機(jī),請(qǐng)把127.0.0.1改成服務(wù)端ip
var socket = io('http://127.0.0.1:3120');
// 當(dāng)連接服務(wù)端成功時(shí)觸發(fā)connect默認(rèn)事件
socket.on('connect', function(){
console.log('connect success');
});
</script>
socket.io主要是通過事件來進(jìn)行通訊交互的。
socket連接除了自帶的connect,message,disconnect三個(gè)事件以外,在服務(wù)端和客戶端開發(fā)者可以自定義其它事件。
服務(wù)端和客戶端都通過emit方法觸發(fā)對(duì)端的事件。
例如下面的代碼在服務(wù)端定義了一個(gè)chat message
事件,事件參數(shù)為$msg
。
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use PHPSocketIO\SocketIO;
$io = new SocketIO(3120);
// 當(dāng)有客戶端連接時(shí)
$io->on('connection', function($socket)use($io){
// 定義chat message事件回調(diào)函數(shù)
$socket->on('chat message', function($msg)use($io){
// 觸發(fā)所有客戶端定義的chat message from server事件
$io->emit('chat message from server', $msg);
});
});
Worker::runAll();
客戶端通過下面的方法觸發(fā)服務(wù)端的chat message事件。
<script src='//cdn.bootcss.com/socket.io/1.3.7/socket.io.js'></script>
<script>
// 連接服務(wù)端
var socket = io('http://127.0.0.1:3120');
// 觸發(fā)服務(wù)端的chat message事件
socket.emit('chat message', '這個(gè)是消息內(nèi)容...');
// 服務(wù)端通過emit('chat message from server', $msg)觸發(fā)客戶端的chat message from server事件
socket.on('chat message from server', function(msg){
console.log('get message:' + msg + ' from server');
});
</script>
phpsocket.io提供了workerStart事件回調(diào),也就是當(dāng)進(jìn)程啟動(dòng)后準(zhǔn)備好接受客戶端鏈接時(shí)觸發(fā)的回調(diào)。
一個(gè)進(jìn)程生命周期只會(huì)觸發(fā)一次??梢栽谶@里設(shè)置一些全局的事情,比如開一個(gè)新的Worker端口等等。
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use PHPSocketIO\SocketIO;
$io = new SocketIO(9120);
// 監(jiān)聽一個(gè)http端口,通過http協(xié)議訪問這個(gè)端口可以向所有客戶端推送數(shù)據(jù)(url類似http://ip:9191?msg=xxxx)
$io->on('workerStart', function()use($io) {
$inner_http_worker = new Worker('http://0.0.0.0:9191');
$inner_http_worker->onMessage = function($http_connection, $data)use($io){
if(!isset($_GET['msg'])) {
return $http_connection->send('fail, $_GET["msg"] not found');
}
$io->emit('chat message', $_GET['msg']);
$http_connection->send('ok');
};
$inner_http_worker->listen();
});
// 當(dāng)有客戶端連接時(shí)
$io->on('connection', function($socket)use($io){
// 定義chat message事件回調(diào)函數(shù)
$socket->on('chat message', function($msg)use($io){
// 觸發(fā)所有客戶端定義的chat message from server事件
$io->emit('chat message from server', $msg);
});
});
Worker::runAll();
phpsocket.io啟動(dòng)后開內(nèi)部http端口通過phpsocket.io向客戶端推送數(shù)據(jù)參考 web-msg-sender。
socket.io提供分組功能,允許向某個(gè)分組發(fā)送事件,例如向某個(gè)房間廣播數(shù)據(jù)。
1、加入分組(一個(gè)連接可以加入多個(gè)分組)
$socket->join('group name');
2、離開分組(連接斷開時(shí)會(huì)自動(dòng)從分組中離開)
$socket->leave('group name');
$io是SocketIO對(duì)象。$socket是客戶端連接
$data可以是數(shù)字和字符串,也可以是數(shù)組。當(dāng)$data是數(shù)組時(shí),客戶端會(huì)自動(dòng)轉(zhuǎn)換為javascript對(duì)象。
同理如果客戶端向服務(wù)端emit某個(gè)事件傳遞的是一個(gè)javascript對(duì)象,在服務(wù)端接收時(shí)會(huì)自動(dòng)轉(zhuǎn)換為php數(shù)組。
1、向當(dāng)前客戶端發(fā)送事件
$socket->emit('event name', $data);
2、向所有客戶端發(fā)送事件
$io->emit('event name', $data);
3、向所有客戶端發(fā)送事件,但不包括當(dāng)前連接。
$socket->broadcast->emit('event name', $data);
4、向某個(gè)分組的所有客戶端發(fā)送事件
$io->to('group name')->emit('event name', $data);
$io->on('connection', function($socket)use($io){
var_dump($socket->conn->remoteAddress);
});
$socket->disconnect();
當(dāng)我們想指定特定域名的頁面才能連接,可以用$io->origins方法來設(shè)置域名白名單。
$io = new SocketIO(2020);
$io->origins('http://example.com:8080');
多個(gè)域名時(shí)用空格分隔,類似
$io = new SocketIO(2020);
$io->origins('http://workerman.net http://www.wtbis.cn');
自帶心跳,無需手動(dòng)設(shè)置
SSL支持有兩種方法,workerman原生和nginx代理
SSL 要求workerman>=3.3.7 phpsocket.io>=1.1.1
<?php
require_once __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
use PHPSocketIO\SocketIO;
// 傳入ssl選項(xiàng),包含證書的路徑
$context = array(
'ssl' => array(
'local_cert' => '/your/path/of/server.pem',
'local_pk' => '/your/path/of/server.key',
'verify_peer' => false,
)
);
$io = new SocketIO(2120, $context);
$io->on('connection', function($socket)use($io){
echo "new connection coming\n";
});
Worker::runAll();
注意:
1、證書是要驗(yàn)證域名的,所以客戶端鏈接時(shí)要指定域名才能順利的建立鏈接。<br>
2、客戶端連接時(shí)不能再用http方式,要改成https類似下面這樣。
<script>
var socket = io('https://yoursite.com:2120');
//.....
</script>
前提條件及準(zhǔn)備工作:
1、已經(jīng)安裝nginx,版本不低于1.3
2、假設(shè)phpsocket.io監(jiān)聽的是2120端口
3、已經(jīng)申請(qǐng)了證書(pem/crt文件及key文件)放在了/etc/nginx/conf.d/ssl下
4、打算利用nginx開啟443端口對(duì)外提供ssl代理服務(wù)(端口可以根據(jù)需要修改)
nginx配置類似如下:
server {
listen 443;
ssl on;
ssl_certificate /etc/ssl/server.pem;
ssl_certificate_key /etc/ssl/server.key;
ssl_session_timeout 5m;
ssl_session_cache shared:SSL:50m;
ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
location /socket.io
{
proxy_pass http://127.0.0.1:2120;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Real-IP $remote_addr;
}
# location / {} 站點(diǎn)的其它配置...
}
注意:
1、證書是要驗(yàn)證域名的,所以客戶端鏈接時(shí)要指定域名才能順利的建立鏈接。<br>
2、客戶端連接時(shí)不能再用http方式,要改成https類似下面這樣。
<script>
var socket = io('https://yoursite.com');
//.....
</scrip