初學(xué)PHP時(shí)辆布,就想著用PHP做個(gè)聊天室危彩,在同學(xué)面前裝一波<尷尬>。那個(gè)時(shí)候只會(huì)使用簡(jiǎn)單的ajax逐样。于是就用了輪訓(xùn)蜗字,那時(shí)候就在想為啥服務(wù)器就不能推送,想一想我發(fā)一條消息服務(wù)器直接推送給其他在線的同學(xué)多方便【此處省略一百萬字】脂新。
WebSocket 協(xié)議在2008年誕生挪捕,2011年成為國(guó)際標(biāo)準(zhǔn),所有瀏覽器都已經(jīng)支持了争便。所以直接在瀏覽器下寫js就可以和后臺(tái)進(jìn)行交互了级零。
它的最大特點(diǎn)就是,服務(wù)器可以主動(dòng)向客戶端推送信息滞乙,客戶端也可以主動(dòng)向服務(wù)器發(fā)送信息奏纪,是真正的雙向平等對(duì)話鉴嗤,屬于服務(wù)器推送技術(shù)的一種。
簡(jiǎn)單使用示例
<?php
/**
* websocket 簡(jiǎn)單控制多人對(duì)話
* 使用session方案序调,根據(jù)與不同人的醉锅、組的談話設(shè)置session問題
* 并根據(jù)session進(jìn)行迭代推送
* 更好的替代方案是使用redis
*/
//創(chuàng)建websocket服務(wù)器對(duì)象,監(jiān)聽0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9502);
//監(jiān)聽WebSocket連接打開事件
$ws->on('open', function ($ws, $request) {
var_dump($request->fd, $request->get, $request->server);
$ws->push($request->fd, "hello, welcome\n");
});
/**
* 監(jiān)聽WebSocket消息事件
* 每次當(dāng)js push信息的時(shí)候发绢,就會(huì)觸發(fā)這個(gè)函數(shù)
*/
$ws->on('message', function ($ws, $frame) {
echo "Message: {$frame->data}\n";
// $ws->push($frame->fd, "server: {$frame->data}");
// var_dump($ws->connections);
// 獲取所有的鏈接$ws->connections
foreach($ws->connections as $fd)
{
$ws->push($fd, "hello{$fd}\n");
}
});
/**
* 監(jiān)聽WebSocket連接關(guān)閉事件
*/
$ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
});
/**
* 執(zhí)行腳本
*/
$ws->start();
由上面的代碼可以看出swoole中websocket分為5個(gè)步驟
- 首先要?jiǎng)?chuàng)建swoole_websocket_sever實(shí)例硬耍,配置好需要監(jiān)聽的地址和端口等
- socket監(jiān)聽代碼$ws->on()
其中配置了多個(gè)事件 open【監(jiān)聽初次連接】、message【監(jiān)聽用戶推送】边酒、close【監(jiān)聽關(guān)閉事件】等默垄,詳細(xì)的事件請(qǐng)查閱相關(guān)資料T_T!
- 啟動(dòng)websocket甚纲,$ws->start();
碰到的坑:
- webSocket關(guān)于屬性connections報(bào)錯(cuò)
系統(tǒng)缺少pcre組建口锭,而connections迭代器需要依賴這個(gè)庫(kù)
- 安裝pcre-dev(或者pcre-devel)
- 重新編譯安裝swoole
- 當(dāng)突然斷網(wǎng)的時(shí)候,來不及關(guān)閉鏈接怎么辦<有待測(cè)試>
設(shè)置心跳檢測(cè)時(shí)間
array( 'heartbeat_idle_time' => 600, 'heartbeat_check_interval' => 60 );
- 安全規(guī)范
WebSocket 不應(yīng)當(dāng)用于混合的上下文環(huán)境介杆;也就是說鹃操,不應(yīng)該在用HTTPS加載的頁(yè)面中打開非安全版本的WebSocket,反之亦然春哨。而實(shí)際上一些瀏覽器也明確禁止這一行為荆隘,包括 Firefox 8 及更高版本。
- 關(guān)于客戶端連接ID:fd
官方文檔解釋:
- $fd是TCP客戶端連接的標(biāo)識(shí)符赴背,在Server程序中是唯一的
- fd 是一個(gè)自增數(shù)字椰拒,范圍是1 ~ 1600萬,fd超過1600萬后會(huì)自動(dòng)從1開始進(jìn)行復(fù)用
- $fd是復(fù)用的凰荚,當(dāng)連接關(guān)閉后fd會(huì)被新進(jìn)入的連接復(fù)用【ps:沒有的互用就給他關(guān)閉】
- 正在維持的TCP連接fd不會(huì)被復(fù)用
- fd好像不支持修改滴
swoole之代碼熱更新
疑問
- 客戶端請(qǐng)求之后swoole是怎樣運(yùn)行的
首先:
一個(gè)最基礎(chǔ)的Swoole Server燃观,需要有3個(gè)進(jìn)程,分別是Master進(jìn)程便瑟、Manager進(jìn)程和Worker進(jìn)程缆毁。
其次:
事實(shí)上,一個(gè)多進(jìn)程模式下的Swoole Server中到涂,有且只有一個(gè)Master進(jìn)程脊框;有且只有一個(gè)Manager進(jìn)程;卻可以有n個(gè)Worker進(jìn)程践啄。
首先浇雹,Master進(jìn)程是一個(gè)多線程進(jìn)程,其中有一組非常重要的線程屿讽,叫做Reactor線程(組)昭灵,每當(dāng)一個(gè)客戶端連接上服務(wù)器的時(shí)候,都會(huì)由Master進(jìn)程從已有的Reactor線程中,根據(jù)一定規(guī)則挑選一個(gè)虎锚,專門負(fù)責(zé)向這個(gè)客戶端提供維持鏈接硫痰、處理網(wǎng)絡(luò)IO與收發(fā)數(shù)據(jù)等服務(wù)。
如圖:
閱讀這篇Swoole的進(jìn)程模型你可能會(huì)對(duì)本疑問解惑
小demo[可以根據(jù)自己的需要改的更好點(diǎn)]
WebSocket.php
<?php
class WebSocket
{
public $server;
public function __construct()
{
$this->server = new swoole_websocket_server("0.0.0.0", 9502);
$this->server->on('open', array($this, 'onOpen'));
$this->server->on('message', array($this, 'onMessage'));
$this->server->on('close', [$this, 'onClose']);
$this->server->start();
}
/**
* Open the connection
*
* @param swoole_websocket_server $server
* @param [type] $request
* @return void
*/
public function onOpen(swoole_websocket_server $server, $request)
{
//
}
/**
* Receive messages
* 當(dāng)客戶端調(diào)用send的時(shí)候觸發(fā)message回掉函數(shù)
* @param swoole_websocket_server $server
* @param [type] $frame
* @return void
*/
public function onMessage(swoole_websocket_server $server, $frame)
{
$result = json_decode($frame->data);
switch ($result->type) {
case "connect":
$data = "Welcome {$result->text} to join us";
$this->iteration($server->connections, $data, "connect");
break;
case "message":
$this->iteration($server->connections, $result->text, "message");
break;
}
}
/**
* disconnect
*
* @param [type] $ser
* @param [type] $fd
* @return void
*/
public function onClose($ser, $fd)
{
echo "the cline {$fd} is close";
}
/**
* Radio
*
* @param [type] $connect
* @param [type] $data
* @param [type] $status
* @return void
*/
public function iteration($connect, $data, $status)
{
foreach ($connect as $fd) {
// console.log();
$this->server->push($fd, $this->JSON(array(
"type" => $status,
"data" => $data
)));
}
}
}
$server = new WebSocket();
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<!-- <title>精美CSS3聊天窗口DEMO演示</title> -->
<title>精美CSS3聊天窗口</title>
<link rel="stylesheet" href="css/style.css" media="screen" type="text/css" />
<style>
</style>
</head>
<body>
<div id="convo" data-from="Sonu Joshi">
<ul class="chat-thread">
<li class="left">Are we meeting today?</li>
<li class="right">yes, what time suits you?</li>
</ul>
<div class="inputButton">
<div>
<input class="input" type="text" placeholder="Search for...">
<input class="submit" type="button" value="Submit">
</div>
</div>
</div>
<div style="text-align:center;clear:both">
</div>
<div class="credits">
<a >Original Pen</a> by
<a >clintioo</a>
</div>
<script>
var dialog = document.querySelector('ul');
var infoInput = document.querySelectorAll("input");
var wsServer = 'ws://127.0.0.1:9502';
var websocket = new WebSocket(wsServer);
websocket.onopen = (evt) => {
console.log(evt);
websocket.send(JSON.stringify({
type: "connect",
text: "I‘m fine窜护,Think you",
// "id":"",
// date: ""
}));
};
websocket.onmessage = (evt) => {
infoInput[0].value = "";
console.log(evt);
var info = eval("(" + evt.data + ")");
var createDom = document.createElement("li");
createDom.innerHTML = info['data'];
createDom.className = "left";
dialog.append(createDom);
}
websocket.onerror = function (evt, e) {
console.log('Error occured: ' + evt.data);
};
infoInput[1].addEventListener("click", () => {
if (infoInput[0].value) {
websocket.send(JSON.stringify({
type: "message",
text: infoInput[0].value,
}));
} else {
alert("發(fā)送信息不允許為空不允許為空");
}
});
document.onkeydown = (event) => {
var e = event || window.event || arguments.callee.caller.arguments[0];
if(e && e.keyCode==13){
infoInput[1].click();
}
};
</script>
</body>
</html>
效果圖
使用的是swoole擴(kuò)展
時(shí):2018-06-07