使用workerman實現在線聊天的方法

使用workerman實現在線聊天的方法

workerman 是一個php編寫的通訊服務。之前的項目都是用它做數據接口服務

這次用它做一個簡單的在線聊天室~

1.下載最新版本的workerman

可以去http://www.workerman.net 去下載

我這里將service 和 client 分開了兩個文件夾,方便管理

大致的項目結構如下。

使用workerman實現在線聊天的方法

客戶端:

客戶端就簡單了。一個簡單的html代碼。嵌入了一個 websocket 監聽服務

var?ws,?name,?client_list={}; function?connect()?{ ???????//?創建websocket ???????ws?=?new?WebSocket("ws://192.168.0.88:2345"); ???????//?當socket連接打開時,輸入用戶名 ???????ws.onopen?=?onopen; ???????//?當有消息時根據消息類型顯示不同信息 ???????ws.onmessage?=?onmessage; ???????ws.onclose?=?function()?{ ??????????console.log("連接關閉,定時重連"); ??????????connect(); ???????}; ???????ws.onerror?=?function()?{ ??????????console.log("出現錯誤"); ???????}; ????}

實現websocket的 打開,message的監聽,以及close?

1、當打開一個客戶端,則立馬彈出一個輸入姓名的對話框

function?onopen(){ ????????//console.log(name); ????????//var?username=connect_id=""; ????????if(!name) ????????{ ????????????name=prompt("請輸入您的名字",""); ????????????if(!name?||?name=='null'){? ????????????????name?=?'咕噠子'; ????????????} ????????} ? ????????$('#curuser').text(name); ? ?????????data='{"type":"1","user":"'+name+'"}'; ????????? ????????ws.send(data); ????}

并將數據推送給服務端。type =1 代表登陸。

2、當收到消息時,判斷消息類型,是群發消息 還是私聊消息。進而處理。

另外,每次用戶有新用戶登陸上來,都會 給各個客戶端推送,用戶列表。進行渲染

function?onmessage(e){ ????????//console.log(e.data); ????????var?data?=?eval("("+e.data+")"); ????????var?info=$('#chatinfo').html(); ????????if(data.type==1) ????????????$('#chatinfo').html(info+'<br>'+data.data); ????????else?if(data.type==2) ????????{ ????????????//?在線用戶列表?userinfo ????????????$('#userinfo').html(data.data); ????????} ????????else?if(data.type==3) ????????{ ????????????//?在線用戶列表?個人信息 ????????????name=data.data.userinfo; ????????????//console.log(data.data); ????????} ????}

?然后另外就是 每個用戶發送消息的代碼了。可以是私聊 ,也可以是群發

$('#send').click(function(e){ ????????var?msg=$('#msg').val(); ????????var?tofriend=$('#tofriend').val(); ????????var?tofriendname=$('#tofriendname').val(); ????????if(tofriend!="") ????????{ ????????????data='{"type":"3","user":"'+name+'","msg":"'+msg+'","friend_id":"'+tofriend+'","friendname":"'+tofriendname+'"}'; ????????}else{ ????????????data='{"type":"2","user":"'+name+'","msg":"'+msg+'"}'; ????????} ????????ws.send(data); ????????$('#msg').attr("value",''); ????});

客戶端差不多就是這樣的了。

客戶端,有幾個坑 ,

坑1、變量名是 name ?則刷新網頁不會被重置,否則就會被重置。(后面查資料發現,這個name變量 ?是 window.name 。所以刷新網頁 該值也不會被刷新掉)

坑2、JS組數組,變量要用”” 最外層為” 如:data='{“type”:”1″,”user”:”‘+name+'”}’; ?否則解析出問題。不能倒過來!

服務端:

服務端主要是workerman 組件 以及?使用 channel分布式通訊組件 實現訂閱 和集群推送 分組推送 以及私聊。

首先,當然是監聽,啟用一個worker的websocket監聽

//?創建一個Worker監聽2346端口,使用websocket協議通訊 $ws_worker?=?new?Worker("websocket://0.0.0.0:2345"); $channel_server?=?new?ChannelServer('0.0.0.0',?2206); //?啟動4個進程對外提供服務 $ws_worker-&gt;count?=?4; $ws_worker-&gt;name="kinmoschat";

在workerman 監聽啟用的時候,進行 channel通訊的注冊。

$ws_worker-&gt;onWorkerStart=function($ws_worker) { ????//?channel?客戶端鏈接上?服務器 ????ChannelClient::connect('127.0.0.1',2206); ????$event_name='私聊'; ????//?訂閱?worker-<id>connections); ????????$to_connect_id=$event_data['to_connection_id']; ????????$message=$event_data['content']; ? ????????foreach?($ws_worker-&gt;connections?as?$connection)?{ ? ????????????if($connection-&gt;id==$to_connect_id) ????????????{ ????????????????$connection-&gt;send($message); ????????????} ????????????????? ????????} ? ????????//?if(!isset($ws_worker-&gt;connections[$to_connect_id])) ????????//?{ ????????//?????echo?'connect?is?not?existn'; ????????//?????return; ????????//?} ????????//?$to_connection=$ws_worker-&gt;connections[$to_connect_id]; ????????//?$to_connection-&gt;send($message); ????}); ? ????//?訂閱廣播事件 ????$event_name?=?'廣播'; ????//?收到廣播?向所有客戶端發送消息 ????ChannelClient::on($event_name,function($event_data)use($ws_worker){ ????????//print_r($event_data); ????????$message=$event_data['content']; ????????foreach?($ws_worker-&gt;connections?as?$connection)?{ ????????????$connection-&gt;send($message); ????????} ????}); };</id>

注冊兩個事件,一個廣播事件,一個私聊事件,用以上線通知的廣播,以及群發消息。私聊 就是私聊了。。這里,還可以做 分組的群發。不過,這個版本還未實現。

然后是針對,客戶端鏈接的回調。

$ws_worker-&gt;onConnect=function($connection){ ????$connection-&gt;id?=?md5($connection-&gt;id."_".time()."_".rand(10000,99999)); };

這里,客戶端回調,我會將客戶端的 ?connectid修改掉。一個簡單的md5 主要是為了防止 流水id太容易被利用吧。。

然后,整個項目的主體,服務端消息的處理回調。

針對每個進來的客戶端,分配一個唯一 id

維護一個 connectid=>user 的關系表

由于開啟了多個進程導致 存到 Session中無效,故而 打算存到 數據庫

斷開鏈接的時候,刪除數據

$ws_worker-&gt;onMessage?=?function($connection,?$data) { ????$res=array('code'=&gt;200,?'msg'=&gt;'ok',?'data'=&gt;null,'type'=&gt;1); ????//?向客戶端發送hello?$data ????//print_r($data); ????$data=json_decode($data,true); ????//print_r($data); ????if(!isset($data['type'])||empty($data['type']))//?type?1??2 ????{ ????????$res=array('code'=&gt;301,?'msg'=&gt;'消息包格式錯誤',?'data'=&gt;null); ????}else{ ????????switch?($data['type'])?{ ????????????case?'1':?//?客戶端上線消息 ????????????????//print_r($connection-&gt;id); ????????????????? ????????????????if(!isset($data['user'])||empty($data['user'])) ????????????????{ ????????????????????$res=array('code'=&gt;301,?'msg'=&gt;'消息包格式錯誤',?'data'=&gt;null); ????????????????????break; ????????????????} ????????????????//?維護一個數組?保存?用戶?connection_id?=&gt;?user ? ????????????????$dsn='mysql:host=127.0.0.1;dbname=kinmoschat;'; ????????????????$pdo=new?PDO($dsn,'root','123456'); ????????????????//準備SQL語句 ????????????????$sql?=?"INSERT?INTO?`user`(`connect_id`,`username`)?VALUES?(:connect_id,:username)"; ? ????????????????//調用prepare方法準備查詢 ????????????????$stmt?=?$pdo-&gt;prepare($sql); ? ????????????????//傳遞一個數組為預處理查詢中的命名參數綁定值,并執行SQL ????????????????$stmt-&gt;execute(array(':connect_id'?=&gt;?$connection-&gt;id,':username'?=&gt;?$data['user'])); ????????????????//獲取最后一個插入數據的ID值 ????????????????//echo?$pdo-&gt;lastInsertId()?.?'<br>'; ? ????????????????//?向自己推送一條消息 ????????????????$res2['type']=3;//?系統信息 ????????????????$res2['data']=array('userinfo'?=&gt;$data['user']);//?系統信息 ????????????????$connection-&gt;send(json_encode($res2)); ? ????????????????$msg="用戶?".$data['user']."?上線了~~"; ????????????????$res['data']=$msg; ????????????????break; ????????????case?'2':?//?客戶端群發送消息 ????????????????if(!isset($data['user'])||empty($data['user'])||!isset($data['msg'])||empty($data['msg'])) ????????????????{ ????????????????????$res=array('code'=&gt;301,?'msg'=&gt;'消息包格式錯誤',?'data'=&gt;null); ????????????????????break; ????????????????} ????????????????$msg="用戶?".$data['user']."說:".$data['msg']; ????????????????$res['data']=$msg; ????????????????break; ????????????case?'3':?//?客戶端私聊 ????????????????if(!isset($data['user'])||empty($data['user'])||!isset($data['msg'])||empty($data['msg'])||!isset($data['friend_id'])||empty($data['friend_id'])) ????????????????{ ????????????????????$res=array('code'=&gt;301,?'msg'=&gt;'消息包格式錯誤',?'data'=&gt;null); ????????????????????break; ????????????????} ????????????????$msg="用戶?".$data['user']."對您說:".$data['msg']; ????????????????$res['data']=$msg; ????????????????$res['type']=1;//?聊天消息 ????????????????$res1=json_encode($res); ????????????????//?推送給單個用戶 ????????????????$event_name?=?'私聊'; ????????????????ChannelClient::publish($event_name,?array( ????????????????????'content'??????????=&gt;?$res1, ????????????????????'to_connection_id'?=&gt;$data['friend_id'] ????????????????)); ????????????????//?另外還要給自己推條消息 ????????????????$msg="您對?".$data['friendname']."說:".$data['msg']; ????????????????$res['data']=$msg; ????????????????$res['type']=1;//?聊天消息 ????????????????$res2=json_encode($res); ????????????????ChannelClient::publish($event_name,?array( ????????????????????'content'??????????=&gt;?$res2, ????????????????????'to_connection_id'?=&gt;$connection-&gt;id ????????????????)); ????????????????return; ????????????????break; ????????????? ????????????default: ????????????????#?code... ????????????????break; ????????} ????} ????$res['type']=1;//?聊天消息 ????$res=json_encode($res); ????//?廣播給所有客戶端 ????$event_name?=?'廣播'; ????ChannelClient::publish($event_name,?array( ????????'content'??????????=&gt;?$res ????)); ? ????$dsn='mysql:host=127.0.0.1;dbname=kinmoschat;'; ????$dbh=new?PDO($dsn,'root','123456'); ????$stmt=$dbh-&gt;query('SELECT?connect_id,username?FROM?user'); ????$row=$stmt-&gt;fetchAll(); ????$uerHtml=""; ????foreach?($row?as?$key?=&gt;?$value)?{ ? ????????$uerHtml.='<a>'.$value['username'].'</a><br>'; ????} ????//print_r($row); ????$res1['type']=2;//?用戶消息 ????$res1['data']=$uerHtml; ????$res1=json_encode($res1); ????? ? ????$event_name?=?'廣播'; ????ChannelClient::publish($event_name,?array( ????????'content'??????????=&gt;?$res1 ????)); };

這里會將每個用戶的 ?connectid=>name 存入數據庫。。

當收到一條 上線消息的時候,廣播給所有用戶。

收到一條群發消息。。廣播給所有客戶端。

收到一條私聊消息。則單個推送給自己以及發送的人。

監聽 客戶端關閉事件,當客戶端關閉,刪除用戶表相關記錄

//?關閉鏈接?將數據庫中的該數據刪除 $ws_worker-&gt;onClose=function($connection) { ????//echo?3233; ????$dsn='mysql:host=127.0.0.1;dbname=kinmoschat;'; ????$pdo=new?PDO($dsn,'root','123456'); ????$sql="delete?from?user?where?connect_id='".$connection-&gt;id."'"; ????//print_r($sql); ????$pdo-&gt;exec($sql); };

更多workerman知識請關注workerman教程欄目。

以上就是使用

? 版權聲明
THE END
喜歡就支持一下吧
點贊8 分享