workerman結合laravel開發在線聊天應用的示例代碼

項目背景:javascript

最近因爲公司的業務需求,須要用到聊天功能。並且有比較多的個性化需求須要定製。以前使用別人的聊天組件是基於微擎的。若是要移植到普通的H5在邏輯修改還有定製上存在比較多的困難。爲此只能克服困難,本身搭建一個吧php

什麼是Workerman?css

Workerman是一款 開源 高性能異步 PHP socket即時通信框架 。支持高併發,超高穩定性,被普遍的用於手機app、移動通信,微信小程序,手遊服務端、網絡遊戲、PHP聊天室、硬件通信、智能家居、車聯網、物聯網等領域的開發。 支持TCP長鏈接,支持Websocket、HTTP等協議,支持自定義協議。擁有異步Mysql、異步Redis、異步Http、MQTT物聯網客戶端、異步消息隊列等衆多高性能組件。html

開始實戰吧!java

1.第一步咱們先把workerman裏須要用到的擴展composer下來吧mysql

?
1
2
3
"workerman/gateway-worker": "^3.0",
"workerman/gatewayclient": "^3.0",
"workerman/workerman": "^3.5",

2.第二步咱們到官方網站把demo所有下載下來,而後放到咱們項目中的目錄jquery

圖片中我就把整個項目都放在了HTTP/Controller/Workerman中。程序員

3.第三步咱們須要把把如下3個文件的引用部分修改成如下。否則會報路徑錯誤web

start_businessworker,start_gateway,start_registerajax

?
1
require_once __DIR__ . '/../../../../../vendor/autoload.php' ;

4.修改完成後咱們就能夠在liunx直接運行對應的啓動文件

?
1
php start.php start -d

若是你是在window下就雙擊start_for_win.bat運行

5.運行成功後,你就應該能夠看到如下的界面

到此咱們搭建基於workerman的通訊環境就已經完成。接下來咱們就能夠根據本身的項目需求進行開發。在此向你們重點說明。咱們全部的聊天是邏輯都在目錄中的Events.php進行修改。

---------------------------------華麗分割線---------------------------------------------------

下面我給你們貼一下我編寫的部分份代碼。

Event.php

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
<?php
/**
  * This file is part of workerman.
  *
  * Licensed under The MIT License
  * For full copyright and license information, please see the MIT-LICENSE.txt
  * Redistributions of files must retain the above copyright notice.
  *
  * @author walkor<walkor@workerman.net>
  * @copyright walkor<walkor@workerman.net>
  */
 
/**
  * 用於檢測業務代碼死循環或者長時間阻塞等問題
  * 若是發現業務卡死,能夠將下面declare打開(去掉//註釋),並執行php start.php reload
  * 而後觀察一段時間workerman.log看是否有process_timeout異常
  */
//declare(ticks=1);
 
/**
  * 聊天主邏輯
  * 主要是處理 onMessage onClose
  */
use \GatewayWorker\Lib\Gateway;
 
class Events
{
   /**
    * 做者:何志偉
    * 當客戶端鏈接上來的時候
    * 建立時間:2018/10/25
    * @param $client_id 此ID爲gatewayworker 自動生成ID
    */
   public static function onConnect( $client_id )
   {
     Gateway::sendToClient( $client_id , json_encode( array (
       'type'   => 'init' ,
       'client_id' => $client_id
     )));
   }
 
 
   /**
    * 有消息時
    * @param int $client_id
    * @param mixed $message
    */
   public static function onMessage( $client_id , $message )
   {
     // debug
     echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']} client_id:$client_id session:" .json_encode( $_SESSION ). " onMessage:" . $message . "\n" ;
 
     // 客戶端傳遞的是json數據
     $message_data = json_decode( $message , true);
     if (! $message_data )
     {
       return ;
     }
 
     // 根據類型執行不一樣的業務
     switch ( $message_data [ 'type' ])
     {
       // 客戶端迴應服務端的心跳
       case 'pong' :
         return ;
       // 客戶端登陸 message格式: {type:login, name:xx, room_id:1} ,添加到客戶端,廣播給全部客戶端xx進入聊天室
       case 'login' :
         // 判斷是否有房間號
         if (!isset( $message_data [ 'room_id' ]))
         {
           throw new \Exception( "\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message" );
         }
 
         // 把房間號暱稱放到session中
         $room_id = $message_data [ 'room_id' ];
         $client_name = htmlspecialchars( $message_data [ 'client_name' ]);
         $_SESSION [ 'room_id' ] = $room_id ;
         $_SESSION [ 'client_name' ] = $client_name ;
 
 
         // 獲取房間內全部用戶列表
         $clients_list = Gateway::getClientSessionsByGroup( $room_id );
         foreach ( $clients_list as $tmp_client_id => $item )
         {
           $clients_list [ $tmp_client_id ] = $item [ 'client_name' ];
         }
//        $clients_list[$client_id] = $client_name;
 
         // 轉播給當前房間的全部客戶端,xx進入聊天室 message {type:login, client_id:xx, name:xx}
         $new_message = array ( 'type' => $message_data [ 'type' ], 'client_id' => $client_id , 'client_name' =>htmlspecialchars( $client_name ), 'time' => date ( 'Y-m-d H:i:s' ), 'to' => $message_data [ 'to' ], 'room_id' => $message_data [ 'room_id' ],
           'from' => $message_data [ 'from' ], 'tag' => $message_data [ 'tag' ]);
         Gateway::sendToGroup( $room_id , json_encode( $new_message ));
         Gateway::joinGroup( $client_id , $room_id );
 
         // 給當前用戶發送用戶列表
         $new_message [ 'client_list' ] = $clients_list ;
         Gateway::sendToCurrentClient(json_encode( $new_message ));
         return ;
 
       // 客戶端發言 message: {type:say, to_client_id:xx, content:xx}
       case 'say' :
         // 非法請求
         if (!isset( $_SESSION [ 'room_id' ]))
         {
           throw new \Exception( "\$_SESSION['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']}" );
         }
         $room_id = $_SESSION [ 'room_id' ];
         $client_name = $_SESSION [ 'client_name' ];
 
         // 私聊
//        if($message_data['to_client_id'] != 'all')
//        {
//          $new_message = array(
//            'type'=>'say',
//            'from_client_id'=>$client_id,
//            'from_client_name' =>$client_name,
//            'to_client_id'=>$message_data['to_client_id'],
//            'content'=>"<b>對你說: </b>".nl2br(htmlspecialchars($message_data['content'])),
//            'time'=>date('Y-m-d H:i:s'),
//          );
//          Gateway::sendToClient($message_data['to_client_id'], json_encode($new_message));
//          $new_message['content'] = "<b>你對".htmlspecialchars($message_data['to_client_name'])."說: </b>".nl2br(htmlspecialchars($message_data['content']));
//          return Gateway::sendToCurrentClient(json_encode($new_message));
//        }
 
         $new_message = array (
           'type' => 'say' ,
           'from_client_id' => $client_id ,
           'from_client_name' => $client_name ,
           'to_client_id' => 'all' ,
           'content' => nl2br (htmlspecialchars( $message_data [ 'content' ])),
           'time' => date ( 'Y-m-d H:i:s' ),
 
         );
         return Gateway::sendToGroup( $room_id ,json_encode( $new_message ));
     }
   }
   /**
    * 當客戶端斷開鏈接時
    * @param integer $client_id 客戶端id
    */
   public static function onClose( $client_id )
   {
     // debug
     echo "client:{$_SERVER['REMOTE_ADDR']}:{$_SERVER['REMOTE_PORT']} gateway:{$_SERVER['GATEWAY_ADDR']}:{$_SERVER['GATEWAY_PORT']} client_id:$client_id onClose:''\n" ;
 
     // 從房間的客戶端列表中刪除
     if (isset( $_SESSION [ 'room_id' ]))
     {
       $room_id = $_SESSION [ 'room_id' ];
       $new_message = array ( 'type' => 'logout' , 'from_client_id' => $client_id , 'from_client_name' => $_SESSION [ 'client_name' ], 'time' => date ( 'Y-m-d H:i:s' ));
       Gateway::sendToGroup( $room_id , json_encode( $new_message ));
     }
   }
 
}

客戶端頁面

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<!DOCTYPE html>
< html lang = "en" >
< head >
   < meta charset = "UTF-8" >
   < title >與{{$to->name}}的對話</ title >
   < script type = "text/javascript" src = "{{asset('js')}}/swfobject.js" ></ script >
   < script type = "text/javascript" src = "{{asset('js')}}/web_socket.js" ></ script >
   < script type = "text/javascript" src = "{{asset('js')}}/jquery.min.js" ></ script >
   < link href = "{{asset('css')}}/jquery-sinaEmotion-2.1.0.min.css" rel = "external nofollow" rel = "stylesheet" >
   < link href = "{{asset('css')}}/bootstrap.min.css" rel = "external nofollow" rel = "stylesheet" >
   < link href = "{{asset('css')}}/style.css" rel = "external nofollow" rel = "stylesheet" >
   < script type = "text/javascript" src = "{{asset('js')}}/jquery-sinaEmotion-2.1.0.min.js" ></ script >
 
   {{--< script src = "https://code.jquery.com/jquery-3.3.1.min.js" ></ script >--}}
 
</ head >
< style >
   #sinaEmotion {
     z-index: 999;
     width: 373px;
     padding: 10px;
     display: none;
     font-size: 12px;
     background: #fff;
     overflow: hidden;
     position: absolute;
     border: 1px solid #e8e8e8;
     top: 100px;
     left: 542.5px;
   }
</ style >
< body onload = "connect();" style = "margin: auto; text-align: center;" >
< div style = "margin: auto;" >
   < div style = "border: 1px solid red; height: 40px; width: 500px; margin: auto;" >
     {{--對話窗口頭部--}}
     < div >
       < div style = "width: 80px; height: 40px; border: 1px solid blue; float: left" >
         < img src="{{$to->heading}}" width="80px" height="40px">
       </ div >
       < div style = "width: 150px; height: 40px; border: 1px solid blue; float: left" >
         {{$to->name}}
       </ div >
     </ div >
     {{--//對話窗口內容--}}
     < div class = "content" style = "width: 500px; height: 400px; border: 1px solid green; margin-top: 40px; overflow-y: auto" >
       {{--對方的頭像與文字--}}
       {{--< div style = "min-height: 50px;margin-top: 10px;" >--}}
         {{--< div style = "width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: left" >--}}
           {{--< img src="{{$to->heading}}" width="50px" height="50px">--}}
         {{--</ div >--}}
         {{--< div style = "border: 1px solid red; float: left; min-height: 50px" >dsadsadsadsadsa</ div >--}}
       {{--</ div >--}}
       {{--個人頭像與文字--}}
       {{--< div style = "min-height:50px;margin-top: 10px;" >--}}
         {{--< div style = "width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: right" >--}}
           {{--< img src="{{$from->heading}}" width="50px" height="50px">--}}
         {{--</ div >--}}
         {{--< div style = "border: 1px solid red; float: right; min-height: 50px" >dsadsadsadsadsa</ div >--}}
       {{--</ div >--}}
     </ div >
     {{--對話發送窗口--}}
     < form onsubmit = "return onSubmit(); return false;" id = "ajaxfrom" >
       < input type = "hidden" name = "to" value="{{$to->id}}">
       < input type = "hidden" name = "from" value="{{$from->id}}">
       < input type = "hidden" name = "room_id" value = "{{$room}}" >
       < input type = "hidden" name = "tag" value = "{{$tag}}" >
       < textarea id = "textarea" name = "content" class = "Input_text" style = "margin: 0px; width: 501px; height: 213px;" ></ textarea >
       < div class = "say-btn" >
         < input type = "button" class = "btn btn-default face pull-left" value = "表情" />
         < button type = "submit" class = "btn btn-default" >發表</ button >
       </ div >
     </ form >
     房間號{{$room}}
   </ div >
</ div >
 
</ body >
</ html >
< script type = "text/javascript" >
   if (typeof console == "undefined") {  this.console = { log: function (msg) { } };}
   // 若是瀏覽器不支持websocket,會使用這個flash自動模擬websocket協議,此過程對開發者透明
   WEB_SOCKET_SWF_LOCATION = "/swf/WebSocketMain.swf";
   // 開啓flash的websocket debug
   WEB_SOCKET_DEBUG = true;
   var ws, name, client_list={};
   var to_client_id="";
 
   // 鏈接服務端初始化函數
   function connect() {
     // 建立websocket 屆時能夠替換爲對應的服務器地址
     ws = new WebSocket("ws://"+document.domain+":7272");
     // 當socket鏈接打開時,輸入用戶名
     ws.onopen = onopen;
     // 當有消息時根據消息類型顯示不一樣信息
     ws.onmessage = onmessage;
     //當鏈接丟失時,調用鏈接方法嘗試從新鏈接
     ws.onclose = function() {
       console.log("鏈接關閉,定時重連");
       connect();
     };
     //當操做報錯時,返回異常錯誤
     ws.onerror = function() {
       console.log("出現錯誤");
     };
 
     //發送ajax獲取當前房間的通話記錄
     $.post("/get_record", { "room":"{{$room}}" },
       function(msg){
         $.each(msg,function (v,k) {
           console.log(k);
           //判斷
           if(k.tag!="{{$tag}}"){
             $(".content").append(
               '< div style = "min-height: 50px;margin-top: 10px;" >' +
               '< div style = "width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: left" >'+
               '< img src="{{$to->heading}}" width="50px" height="50px">'+
               '</ div >'+
               '< div style = "border: 1px solid red; float: left; min-height: 50px" >'+k.content+'</ div >'+
               '< div >'
             ).parseEmotion();
           }else{
             $(".content").append(
               '< div style = "min-height: 50px;margin-top: 10px;" >' +
               '< div style = "width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: right" >'+
               '< img src="{{$from->heading}}" width="50px" height="50px">'+
               '</ div >'+
               '< div style = "border: 1px solid red; float: right; min-height: 50px" >'+k.content+'</ div >'+
               '< div >'
             ).parseEmotion();
           }
         })
       });
   }
 
   // 鏈接創建時發送登陸信息
   function onopen()
   {
     var login_data='{"type":"login","client_name":"{{$from->name}}","room_id":"{{$room}}","to":"{{$to->id}}","from":"{{$from->id}}","tag":"{{$tag}}"}';
 
     ws.send(login_data);
     console.log('登陸成功')
   }
 
   // 服務端發來消息時
   function onmessage(e)
   {
     var data = JSON.parse(e.data);
     switch(data['type']){
       // 服務端ping客戶端心跳
       case 'ping':
         ws.send('{"type":"pong"}');
         break;
       // 登陸 更新用戶列表
       case 'login':
         //講須要的發送ID保存到本地to_client_id變量中
         for(var p in data['client_list']){
           to_client_id=p;
         }
         console.log(to_client_id);
         break;
       // 發言
       case 'say':
         console.log(data);
         say(data['from_client_id'], data['from_client_name'], data['content'], data['time']);
         break;
       // 用戶退出 更新用戶列表
       case 'logout':
         console.log(data);
         break;
       case 'init':
         //此處能夠發送ajax用於綁定不一樣的用戶ID和client
         console.log(data);
         break;
 
     }
   }
 
   // 提交對話
   function onSubmit() {
     //先檢查當前的對話是否超過20條記錄數
     var count=true;
     //發送ajax獲取當前房間的通話記錄
     $.ajax({
       url: "/check_count",
       type: "post",
       async:false,
       // cache: false,
       // contentType: false,
       // processData: false,
       data:{
       'room':"1",
       },
       success: function (msg) {
         if(msg>10){
           alert('當前的對話已經超過次數,請購買對應服務')
           count=false;
         }
       }
 
     });
 
 
     if(count){
       var neirong=$("#textarea").val().replace(/"/g, '\\"').replace(/\n/g,'\\n').replace(/\r/g, '\\r');
       //ajax先把對應的內容發送到後臺錄入,回調成功後才把信息發送
       var fm=$("#ajaxfrom")[0];
       var formData = new FormData(fm);
       $.ajax({
         url: "/record",
         type: "post",
         cache: false,
         contentType: false,
         processData: false,
         data: formData,
         beforeSend:function(){
 
         },
         success: function (msg) {
 
           if(msg.code=="0"){
             ws.send('{"type":"say","to_client_id":"all","to_client_name":"{{$to->name}}","content":"'+neirong+'"}');
             //清空文本框內容
             $("#textarea").val("");
             //強制定位光標
             $("#textarea").focus();
           }else{
 
           }
 
         }
 
       });
     }
 
     return false;
 
 
   }
 
 
 
   // 發言
   function say(from_client_id, from_client_name, content, time){
     //判斷當前的用戶名稱與發送消息的名稱是否一致
     if( "{{$from->name}}" == from_client_name){
       $(".content").append(
         '< div style = "min-height: 50px;margin-top: 10px;" >' +
         '< div style = "width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: right" >'+
         '< img src="{{$from->heading}}" width="50px" height="50px">'+
         '</ div >'+
         '< div style = "border: 1px solid red; float: right; min-height: 50px" >'+content+'</ div >'+
         '< div >'
       ).parseEmotion();
     }else{
       $(".content").append(
         '< div style = "min-height: 50px;margin-top: 10px;" >' +
         '< div style = "width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: left" >'+
         '< img src="{{$to->heading}}" width="50px" height="50px">'+
         '</ div >'+
         '< div style = "border: 1px solid red; float: left; min-height: 50px" >'+content+'</ div >'+
         '< div >'
       ).parseEmotion();
     }
 
     // $("#dialog").append('< div class = "speech_item" >< img src = "http://lorempixel.com/38/38/?'+from_client_id+'" class = "user_icon" /> '+from_client_name+' < br > '+time+'< div style = "clear:both;" ></ div >< p class = "triangle-isosceles top" >'+content+'</ p > </ div >').parseEmotion();
   }
   $(function(){
     //全局用戶ID
     select_client_id = 'all';
 
     //若是發送的用戶有變化則對應的用戶ID進行替換
     $("#client_list").change(function(){
       select_client_id = $("#client_list option:selected").attr("value");
     });
     //表情選擇
     $('.face').click(function(event){
       $(this).sinaEmotion();
       event.stopPropagation();
     });
   });
 
   // document.write('< meta name = "viewport" content = "width=device-width,initial-scale=1" >');
   $("textarea").on("keydown", function(e) {
     //按enter鍵自動提交
     if(e.keyCode === 13 && !e.ctrlKey) {
       e.preventDefault();
       $('form').submit();
       return false;
     }
 
     // 按ctrl+enter組合鍵換行
     if(e.keyCode === 13 && e.ctrlKey) {
       $(this).val(function(i,val){
         return val + "\n";
       });
     }
   });
 
</ script >
 
 
 
 
 
相關文章
相關標籤/搜索