thinkphp5部署workerman長連接

我的博客文章網址:

http://www.jloongking.cn/tp50/public/blog/index/blog?blogid=93

由于最近做一個物聯網項目,該項目需要遠程將溫度推送到服務器,并由服務器推送到web前臺,硬件可以利用tcp協議將數據上傳到到服務器,但是由于不固定ip的原因,服務器是找不到web前端的,而在這個時候我們就需要利用到長連接workerman,在之前我曾經利用workerman進行聊天軟件的編寫,實現多人在線聊天功能,而在這之前我沒有將這個博客編寫成功,本次趁著這個機會,我將長連接的知識進行了復習,正好博客完成時間沒有多久,于是將這次經歷記錄在案,以備以后自己查看學習,也方便了看到這個博文并且可能需要用到該框架的同學們。最后,我需要感謝一位同學,他在這個項目中幫助了我很多,。

在進行該教程之前,我們需要了解一下workerman這個框架和thinkphp這個框架,thinkphp框架是PHP幾大框架之一,想要了解的同學們可以到ThinkPHP5.0完全開發手冊上進行文檔的閱讀,以下簡稱tp5,tp5框架是國產為數不多的優秀框架之一,國產的哦!

而workerman則是在workerman官網上有詳細的介紹,同學們可以到該網站上進行手冊的查看。

ThinkPHP5.0完全開發手冊里面有一篇介紹他們兩個融合的文章composer包workerman在這篇文章里介紹了tp5融合workerman的教程,但是介紹的過于簡潔,我試驗了兩次并沒有走通,而在這次的記錄中我將我的融合過程和這個方法進行對比,分析以前沒有走通的原因。

不論在哪個方法中,我們都需要將wokerman的包引入,我們需要用到composer,沒有安裝composer的同學需要自行安裝。

第一步

導入workerman包:

composer?require?topthink/think-worker

如果windows服務器還要利用以下命令:

composer?require?workerman/workerman-for-win

如運行出現錯誤PHP Fatal error: Call to undefined function Workerman\Lib\pcntl_signal(),需要刪除vendor\workerman\workerman,防止命名覆蓋。

當包引入完成后,我們會在項目根目錄下的vendor文件夾下看到workerman文件夾,這樣框架就引入了該項目,我們下一步需要配置服務的啟動和引用。

第二步

在這一步中我們需要將啟動服務文件放入到項目根目錄中,在根目錄中我們新建啟動服務文件server.php

代碼如下:

#!/usr/bin/env?php

<?php

define('APP_PATH',?__DIR__?.?'/application/');

//這是原來的代碼

//define('BIND_MODULE','push/Worker');

//這是我修改后的代碼

define('BIND_MODULE','push/Workertest/index');

//?加載框架引導文件

require?__DIR__?.?'/thinkphp/start.php';

這個代碼的意思是綁定workerman的模塊是/application/push/Worker這個控制器,但是由于我們沒有用tp5這個框架的引用方式這里我們將代碼改為如上所示,直接進入到控制器的index方法,在下一步我們會介紹為什么用這種方法。我們在下一步中就會定義模塊,也就是實現功能的地方:/application/push/workertest.php

第三步

在這一步開始前我們來看一下tp5開發手冊中的worker.php,需要聲明的是我并沒用利用該方法。

????一共有一個變量和5個方法,變量是定義端口和域名的,方法是分別為,連接上時,服務開始時,接到信息時,錯誤時,斷開時的相應處理方法。

代碼如下

<?php

namespace?app\push\controller;

use?think\worker\Server;

class?Worker?extends?Server{????


????protected?$socket?=?'websocket://push.app:2346';??

?????/**

?????*?收到信息

?????*?@param?$connection

?????*?@param?$data

?????*/

????public?function?onMessage($connection,?$data)

????{

????????$connection->send('我收到你的信息了');

????}????


????/**

?????*?當連接建立時觸發的回調函數

?????*?@param?$connection

?????*/

????public?function?onConnect($connection)

????{

????}????


????/**

?????*?當連接斷開時觸發的回調函數

?????*?@param?$connection

?????*/

????public?function?onClose($connection)

????{


????}????


????/**

?????*?當客戶端的連接上發生錯誤時觸發

?????*?@param?$connection

?????*?@param?$code

?????*?@param?$msg

?????*/

????public?function?onError($connection,?$code,?$msg)

????{???????

?????????echo?"error?$code?$msg\n";

????}????


????/**

?????*?每個進程啟動

?????*?@param?$worker

?????*/

????public?function?onWorkerStart($worker)

????{

????}

}

如果用tp5給出的方法,我們需要在其他的控制器中實例化該控制器類,而在我用到該框架時沒有看出該用法,誤以為實現功能直接在該控制器中調用即可,當我利用到tcp鏈接時出現了兩個鏈接的同時調用,在tp5文檔中的這個方法,我無法同時實例化兩個鏈接,于是我放棄了該方法,想要研究的同學可以繼續研究一下,下面給出我的方法。

workertest.php代碼如下:

<?php

namespace?app\push\controller;

use?Workerman\Worker;

use?Workerman\Lib\Timer;

use?Workerman\Connection\AsyncTcpConnection;

class?WorkerTest

{

????private?$connections;

????private?$connection_to_ws;

????public?function?index()

????{

????????//?$connections?=?array();?

????????$socket?=?new?Worker('websocket://0.0.0.0:2346');

????????//?設置transport開啟ssl,websocket+ssl即wss

????????//?$socket->transport?=?'ssl';

????????//?啟動1個進程對外提供服務??

????????$socket->count?=?1;

????????//給這個進程設置一個array()

????????//?當有客戶端連接時

????????$socket->onConnect?=?function($connection)

????????{

????????????var_dump(count($this->connections));

????????????$connection->send("lianjie");

????????????$this->connections[$connection->id]=$connection;

????????};

????????//?當有客戶端連接時

????????$socket->onMessage?=?function($connection,$data)

????????{

????????????//?var_dump($data);

????????????//?var_dump(json_decode($data));

????????????$jdata?=?json_decode($data);

????????????if(isset($jdata->tem))

????????????{

????????????????foreach($this->connections?as?$con){

????????????????????if(isset($con->endno)&&isset($jdata->endno)&&$con->endno==$jdata->endno){

????????????????????????$con->send($jdata->tem);

????????????????????}

????????????????}

????????????}

????????????else

????????????{

????????????????$connection->send("數據已接受");

????????????????$connection->endno=$jdata->endno;

????????????????$this->connections[$connection->id]=$connection;

????????????}

????????};

????????//?當有客戶端連接斷開時

????????$socket->onClose?=?function($connection)

????????{

????????????if(isset($connection->id))

????????????{

????????????????//?連接斷開時刪除映射

????????????????unset($this->connections[$connection->id]);

????????????}

????????};

????????$tcp?=?new?Worker('tcp://0.0.0.0:8282');

????????$tcp->onMessage?=?function($connection,?$data)

????????{

????????????if(is_null($this->connection_to_ws))

????????????{

????????????????var_dump('connect');

????????????????$this->connection_to_ws?=?new?AsyncTcpConnection('ws://119.29.170.92:2346');

????????????????$this->connection_to_ws->connect();

????????????}

????????????$this->connection_to_ws->send($data);

????????????//?var_dump(count($this->connections));

????????????//??foreach($this->connections?as?$con){

????????????//??????if($con->endno==json_decode($data)->endno){

????????????//??????????$con->send(json_decode($data)->tem);

????????????//??????}

????????????//??}

????????????};

????????//?運行worker??

????????Worker::runAll();

????????}

}

每當前端瀏覽器通過websoket上傳給服務器對應終端號,workerman就會將本鏈接放入到隊列,與此同時,我通過tcp獲取到硬件的值,并經過AsyncTcpConnection這個workerman對象將由tcp端口獲取的值轉發給websoket端口,再由websoket進行遍歷當前隊列鏈接的前端瀏覽器,通過終端id查找并推送給對應的前端瀏覽器。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 英文文檔,一開始我也是抗拒的,邊翻譯邊看,也就花費了1個小時基本就閱讀過了,我的英文基礎其實很差。附上鏈接:鏈接:...
    lonecolonel閱讀 10,003評論 3 1
  • _________________________________________________________...
    fastwe閱讀 649評論 0 0
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,424評論 0 10
  • =========================================================...
    lavor閱讀 3,506評論 0 5
  • 小姨俊香已經和姥姥冷戰一年多了。 在姥姥眼里,小姨就是一個不折不扣的白眼狼,在小姨看來,姥姥才算得上冷血動...
    肉多多肉閱讀 398評論 1 2