由于自己寫了一個(gè)博客,一直對workman有想法的我邦尊,突發(fā)奇想的想在博客里面弄個(gè)聊天室,因此就研究了一下workman,看了下workman的手冊链峭,然后了解了一下又沾,workman,發(fā)現(xiàn)他是通過socket進(jìn)行通訊杖刷,由于socket是tcp層的,因此可以進(jìn)行長鏈接役听,接著雙向通訊不瓶,而不像php的普通情況蚊丐,通過http或者h(yuǎn)ttps進(jìn)行通訊艳吠,因?yàn)閔ttp或者h(yuǎn)ttps每次通訊需要三次握手,四次揮手凛篙,而且服務(wù)器始終是處于被動(dòng)狀態(tài)栏渺,無法主動(dòng)和客戶端進(jìn)行交流磕诊,因此若是客戶端不進(jìn)行輪詢的情況下,幾乎是做不到實(shí)時(shí)通訊滞磺。
而workman可以做到击困,workman是用PHP開發(fā)的广凸,他就是用來調(diào)用socket來進(jìn)行通訊的蛛枚。此外還需要補(bǔ)充一點(diǎn)就是蹦浦,workman他存在的一個(gè)缺陷企蹭,由于他本身是屬于單線程的谅摄,如果你的服務(wù)器是多線程的,那workman是始終無法占滿cpu的顽照,所以對于多線程的服務(wù)器來說闽寡,workman的工作效率的偏低的爷狈,因此考慮到多線程的問題,設(shè)計(jì)workman的大佬們思币,又設(shè)計(jì)了一款workman的升級(jí)版羡微,gateway妈倔,它是屬于多線程的,如果用戶量大的話毅哗,只要CPU處理的過來,他就完全能繼續(xù)扛著随夸,其實(shí)gateway的設(shè)計(jì)原理就是:你有幾個(gè)線程,我就開幾個(gè)work滿。
廢話不多說酥艳,接著開始說workman.
下載和安裝workman就不說了充石,直接說如何使用吧霞玄。
由于workman的啟動(dòng)是需要命令框的坷剧,因此在laravel中,有專門用來生成自定義命令框的一個(gè)模塊撕瞧,commond狞尔,具體的話偏序,可以去laravel中查看。
php artisan make:command? WorkermanCommand? (創(chuàng)建自己的 laravel Artisan Console);
接著就在handle()方法中寫上寇漫,你的命令邏輯。
我自己定義的命令邏輯是 wk? {action}? :action 就是你要執(zhí)行的方法逸月。
例如碗硬,我需要執(zhí)行start ,就可以寫成: php artisan wk start
從handle()方法中看到瓢颅,當(dāng)action為start 的時(shí)候挽懦,調(diào)用chat_room()這個(gè)方法。
可以看到執(zhí)行的邏輯就是:
1.先實(shí)例化workman.
2.開啟1個(gè)進(jìn)程醒第,至于為什么不開啟多個(gè)進(jìn)程进鸠,那是因?yàn)榭湍辏?dāng)開啟多個(gè)進(jìn)程以后,如果在不同進(jìn)程中司恳,用戶和用戶之間是不能通訊得榔至,因此就只開一個(gè)進(jìn)程就ok了唧取,如果無法理解,就舉個(gè)例子邢享,現(xiàn)在有4個(gè)房間骇塘,然后一堆人站在房間前面韩容,每一個(gè)人只能隨機(jī)選擇一個(gè)房間進(jìn)入群凶,當(dāng)他們進(jìn)入房間后,同一個(gè)房間的人能相互交流赠尾,不同房間的人只能干瞪眼毅弧,沒法交流够坐。(這就是為什么開一個(gè)進(jìn)程的原因)
3.設(shè)置一個(gè)變量崖面,用來存放用戶信息嘶朱。以便于對用戶進(jìn)行通訊發(fā)送消息光酣。
4.當(dāng)用戶連接的時(shí)候救军,發(fā)送信息的時(shí)候唱遭,這時(shí)候獲取到用戶的信息,并且將他存放到定義好的變量中疫鹊,然后將他發(fā)送的信息遍歷給其他的每個(gè)一用戶司致,做到信息的實(shí)時(shí)通訊脂矫。
5.當(dāng)用戶斷開連接的時(shí)候庭再,為了減少workman推送消息的資源,首先吧斷開連接的用戶給剔除了颅围,然后廣播通知其他用戶恨搓,該用戶已經(jīng)退出聊天奶卓。
6.設(shè)置一個(gè)心跳夺姑,看了官方文檔掌猛,主要作用就是為了防止長鏈接長時(shí)間不通訊導(dǎo)致被路由節(jié)點(diǎn)強(qiáng)制斷開,說白了就是需要定期的和路由節(jié)點(diǎn)說一聲:兄弟竹海,我和他還活著丐黄,別我們T了灌闺。 他這邊的心跳功能,就是一個(gè)定時(shí)器甩卓,客戶端長時(shí)間不發(fā)信息給服務(wù)器逾柿,但是服務(wù)器又怕被斷開宅此,咋辦呢诽凌,那服務(wù)器只能侣诵,定時(shí)的給客戶端發(fā)個(gè)消息。這樣就能保持不被斷開了财搁。心跳間隔建議值:建議客戶端發(fā)送心跳間隔小于60秒躬络,比如55秒穷当。(這是官方建議馁菜,我設(shè)置了59秒)
這就是PHP這塊的代碼。
在cmd命令框執(zhí)行:php artisan wk start? ? ? ? ? ? ? ---這樣workman 就啟動(dòng)了峭火。
下面貼一下前端的js代碼卖丸。
這塊是html.就是一個(gè)聊天框.
前端需要處理的很少稍浆,只需要發(fā)送信息和接受消息粹湃。
下面看一下恐仑,成果
這是模擬的兩個(gè)用戶的測試結(jié)果。
當(dāng)然你也可以到www.youngxs.com/boke/boke_chat來測試玩一下为鳄。一個(gè)瀏覽器同時(shí)開兩個(gè)就可以了裳仆。
有不懂的可以咨詢我:601902897? 歡迎來騷擾
下面就是貼下代碼:
<?php
namespace App\Console\Commands;
use Workerman\Worker;
use \Workerman\Lib\Timer;
use Illuminate\Console\Command;
class WorkermanCommand extends Command{
? ? private $server;
? ? /**
* The name and signature of the console command.
*
? ? * @var string
*/
? ? protected $signature = 'wk {action}';
? ? /**
* The console command description.
*
? ? * @var string
*/
? ? protected $description = 'Start a Workerman server.';
? ? /**
* Create a new command instance.
*
? ? * @return void
*/
? ? public function __construct()
{
? ? ? ? parent::__construct();
? ? }
? ? /**
* Execute the console command.
*
? ? * @return mixed
*/
? ? public function handle()
{
? ? ? ? global $argv;? //$argv 在ArgvInput類中被定義了,就是$_SERVER['argv'];
? ? ? ? $arg = $this->argument('action');
? ? ? ? $argv[1] = $argv[2];
? ? ? ? $argv[2] = isset($argv[3]) ? "-{$argv[3]}" : '';
? ? ? ? switch ($arg) {
? ? ? ? ? ? case 'start':
? ? ? ? ? ? ? ? $this->chat_room();
? ? ? ? ? ? ? ? break;
? ? ? ? }
}
? ? private function chat_room()
{
? ? ? ? //建立socket連接
? ? ? ? $http_worker = new Worker('websocket://0.0.0.0:2001');
? ? ? ? // 啟動(dòng)4個(gè)進(jìn)程對外提供服務(wù)
? ? ? ? $http_worker->count = 1;
? ? ? ? //定義一個(gè)屬性孤钦,用來存放用戶的連接信息歧斟,$connection
? ? ? ? $http_worker->uidConnections = array();
? ? ? ? //socket事件類中的,鏈接屬性偏形,調(diào)用閉包函數(shù)
? ? ? ? $http_worker->onConnect = function($connection)use($http_worker)
{
? ? ? ? ? ? // 設(shè)置連接的onMessage回調(diào)
? ? ? ? ? ? $connection->onMessage = function($connection, $data)use($http_worker)
{
? ? ? ? ? ? ? ? //獲取用戶傳輸過來的數(shù)據(jù),并且進(jìn)行json_decode解析
? ? ? ? ? ? ? ? $data=json_decode($data,true);
? ? ? ? ? ? ? ? //獲取用戶ID接著將用戶信息$connection俊扭,存儲(chǔ)在$http_worker->uidConnections中队橙,
? ? ? ? ? ? ? ? $http_worker->uidConnections[$data['id']]=$connection;
? ? ? ? ? ? ? ? //給其他用戶發(fā)送,該用戶發(fā)送的信息萨惑,為了保持?jǐn)?shù)據(jù)的實(shí)時(shí)性捐康。
? ? ? ? ? ? ? ? foreach ($http_worker->uidConnections as $k =>$v){
? ? ? ? ? ? ? ? ? ? if($k != $data['id']){
? ? ? ? ? ? ? ? ? ? ? ? if(isset($data['content'])){
? ? ? ? ? ? ? ? ? ? ? ? ? ? $v->send(json_encode(['status'=>0,'msg'=>$data['content']]));
? ? ? ? ? ? ? ? ? ? ? ? }
}
}
? ? ? ? ? ? };
? ? ? ? ? ? //設(shè)置用戶斷開連接的回調(diào)。
? ? ? ? ? ? $connection->onClose = function($connection)use($http_worker)
{
? ? ? ? ? ? ? ? foreach ($http_worker->uidConnections as $k=>$v){
? ? ? ? ? ? ? ? ? ? if($v==$connection){
? ? ? ? ? ? ? ? ? ? ? ? unset( $http_worker->uidConnections[$k]);
? ? ? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? ? ? $v->send(json_encode(['status'=>0,'msg'=>$k.'下線了']));
? ? ? ? ? ? ? ? ? ? }
}
? ? ? ? ? ? };
? ? ? ? ? ? /*設(shè)置心跳庸蔼,為防止長鏈接斷開? ? ? ? ? ? * 原理:就是設(shè)置一個(gè)定時(shí)器解总,定時(shí)的向客戶端發(fā)送數(shù)據(jù)? ? ? ? ? ? * $time_interval 時(shí)間間隔* */
? ? ? ? ? ? $time_interval = 59;
? ? ? ? ? ? Timer::add($time_interval, function()use($http_worker)
{
? ? ? ? ? ? ? ? if(count($http_worker->uidConnections)>0){
? ? ? ? ? ? ? ? ? ? foreach ($http_worker->uidConnections as $connection) {
? ? ? ? ? ? ? ? ? ? ? ? $connection->send(json_encode(['status'=>1,'smg'=>'socket_hot']));
? ? ? ? ? ? ? ? ? ? }
}
? ? ? ? ? ? });
? ? ? ? };
? ? ? ? Worker::runAll();
? ? }
}
下面的是JS代碼
? ? var id='youngxs_name'+ Date.parse(new Date());
? ? $('.italic').html('游客:'+id)
var url='ws://127.0.01:2001'
? ? var ws =new WebSocket(url);
? ? ws.onopen =function() {
var data={'id':id}
data=JSON.stringify(data);
? ? ? ? ws.send(data);
? ? ? ? $('#chat_sure').click(function () {
var content=$('#chat_send').val();
? ? ? ? ? ? var str=id+'說:'+content;
? ? ? ? ? ? var data={'id':id,'content':str}
data=JSON.stringify(data);
? ? ? ? ? ? ws.send(data);
? ? ? ? ? ? $('#chat_room').append('<p>您說:'+content+'</p>');
? ? ? ? ? ? $('#chat_send').val('')
})
$("#chat_send").keypress(function (e) {
if (e.which ==13) {
var content=$('#chat_send').val();
? ? ? ? ? ? ? ? var str=id+'說:'+content;
? ? ? ? ? ? ? ? var data={'id':id,'content':str}
var $chatRoom =$('#chat_room')
data=JSON.stringify(data);
? ? ? ? ? ? ? ? ws.send(data);
? ? ? ? ? ? ? ? $('#chat_room').append('<p>您說:'+content+'</p>')
$('#chat_send').val('')
$chatRoom[0].scrollTop = $chatRoom[0].scrollHeight-$chatRoom[0].clientHeight;
? ? ? ? ? ? }
});
? ? };
? ? ws.onmessage =function(e) {
var msg=JSON.parse(e.data);
? ? ? ? ? if(msg.status==0){
$('#chat_room').append('<p>'+msg.msg+'</p>');
? ? ? ? ? ? ? var $chatRoom =$('#chat_room')
$chatRoom[0].scrollTop = $chatRoom[0].scrollHeight-$chatRoom[0].clientHeight;
? ? ? ? ? }
};
</script>