websocket初次使用

閏了個土發表於2018-06-11

初學PHP時,就想著用PHP做個聊天室,在同學面前裝一波<尷尬>。那個時候只會使用簡單的ajax。於是就用了輪訓,那時候就在想為啥伺服器就不能推送,想一想我發一條訊息伺服器直接推送給其他線上的同學多方便【此處省略一百萬字】。

WebSocket 協議在2008年誕生,2011年成為國際標準,所有瀏覽器都已經支援了。所以直接在瀏覽器下寫js就可以和後臺進行互動了。

它的最大特點就是,伺服器可以主動向客戶端推送資訊,客戶端也可以主動向伺服器傳送資訊,是真正的雙向平等對話,屬於伺服器推送技術的一種。

簡單使用示例

<?php 
/**
 * websocket 簡單控制多人對話
 * 使用session方案,根據與不同人的、組的談話設定session問題 
 * 並根據session進行迭代推送
 * 更好的替代方案是使用redis
 */


//建立websocket伺服器物件,監聽0.0.0.0:9502埠
$ws = new swoole_websocket_server("0.0.0.0", 9502);

//監聽WebSocket連線開啟事件
$ws->on('open', function ($ws, $request) {
    var_dump($request->fd, $request->get, $request->server);
    $ws->push($request->fd, "hello, welcome\n");
});

/**
 * 監聽WebSocket訊息事件 
 * 每次當js push資訊的時候,就會觸發這個函式
 */
$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");
    }
});

/**
 * 監聽WebSocket連線關閉事件
 */
$ws->on('close', function ($ws, $fd) {
    echo "client-{$fd} is closed\n";
});
/**
 * 執行指令碼
 */
$ws->start();
複製程式碼

由上面的程式碼可以看出swoole中websocket分為5個步驟

  1. 首先要建立swoole_websocket_sever例項,配置好需要監聽的地址和埠等
  2. socket監聽程式碼$ws->on()

其中配置了多個事件 open【監聽初次連線】、message【監聽使用者推送】、close【監聽關閉事件】等,詳細的事件請查閱相關資料T_T!

  1. 啟動websocket,$ws->start();

碰到的坑:

  1. webSocket關於屬性connections報錯

系統缺少pcre組建,而connections迭代器需要依賴這個庫

  • 安裝pcre-dev(或者pcre-devel)
  • 重新編譯安裝swoole
  1. 當突然斷網的時候,來不及關閉連結怎麼辦<有待測試>

設定心跳檢測時間

array(
'heartbeat_idle_time' => 600,
'heartbeat_check_interval' => 60
);
複製程式碼
  1. 安全規範

WebSocket 不應當用於混合的上下文環境;也就是說,不應該在用HTTPS載入的頁面中開啟非安全版本的WebSocket,反之亦然。而實際上一些瀏覽器也明確禁止這一行為,包括 Firefox 8 及更高版本。

  1. 關於客戶端連線ID:fd

官方文件解釋:

  • $fd是TCP客戶端連線的識別符號,在Server程式中是唯一的
  • fd 是一個自增數字,範圍是1 ~ 1600萬,fd超過1600萬後會自動從1開始進行復用
  • $fd是複用的,當連線關閉後fd會被新進入的連線複用【ps:沒有的互用就給他關閉】
  • 正在維持的TCP連線fd不會被複用
  • fd好像不支援修改滴

swoole之程式碼熱更新

swoole之程式碼熱更新實現

疑問

  1. 客戶端請求之後swoole是怎樣執行的

首先:

一個最基礎的Swoole Server,需要有3個程式,分別是Master程式、Manager程式和Worker程式。

其次:

事實上,一個多程式模式下的Swoole Server中,有且只有一個Master程式;有且只有一個Manager程式;卻可以有n個Worker程式。

首先,Master程式是一個多執行緒程式,其中有一組非常重要的執行緒,叫做Reactor執行緒(組),每當一個客戶端連線上伺服器的時候,都會由Master程式從已有的Reactor執行緒中,根據一定規則挑選一個,專門負責向這個客戶端提供維持連結、處理網路IO與收發資料等服務。

如圖:

Swoole的程式/執行緒模型:

閱讀這篇Swoole的程式模型你可能會對本疑問解惑

小demo[可以根據自己的需要改的更好點]

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
     * 當客戶端呼叫send的時候觸發message回掉函式
     * @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 href="http://codepen.io/clintioo/pen/HAkjq">Original Pen</a> by
		<a href="http://codepen.io/clintioo/pen/HAkjq">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("傳送資訊不允許為空不允許為空");
			}
		});

		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擴充套件 時:2018-06-07

相關文章