安装laravel:

composer create-project laravel/laravel=5.4.* --prefer-dist

安装workerman相关扩展:

"workerman/gateway-worker": "^3.0",
"workerman/gatewayclient": "^3.0",
"workerman/workerman": "^3.5"

做了简易的登录页面,数据库:

路由web.php文件:

Route::group(['namespace' => 'Home'], function () {
	Route::get("login","LoginController@login");//登录
	Route::get("dologin","LoginController@dologin");//登录处理
	Route::get("company","ChatController@company");//公司列表
	Route::get("chat/{id}","ChatController@chat");//公司聊天室
});

登录信息处理:LoginController.php中:

<?php

namespace App\Http\Controllers\Home;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class LoginController extends Controller
{
    public function login(Request $request)
    {
        return view('home.login');
    }


    public function dologin(Request $request)
    {
        $input = $request->all();
        $res = \DB::table('user')->where(['name'=>$input['name'], 'passwd'=>$input['passwd']])->first();
        if($res){
            $request->session()->put('loginuser', $res);
            $request->session()->save();
            return redirect('/company');
        }else{
            return redirect('/login');
        }
    }
}

对应登录的前端页面:

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>登录</title>
</head>
<style>
</style>
<body>		
	<form action="{{url('/dologin')}}">
		<div style="width:200px; height:auto; margin:10% auto;">
			<div>账号:<input type="text" value="唉吆喂" name="name" /></div>
			<div>密码:<input type="text" value="123456" name="passwd" /></div>
			<input type="submit" value="登录" style="margin:10px auto; display: block;" />
		</div>
	</form>
</body>
</html>
<script type="text/javascript">
</script>

登录之后进入公司列表页

ChatController.php:

<?php

namespace App\Http\Controllers\Home;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class ChatController extends Controller
{
    /**
    * 公司列表
    * @return [type] [description]
    */
    public function company()
    {
    	$res = \DB::table('company')->get();
    	return view('home.company',compact('res'));
    }

    /**
     * 公司聊天室
     * @param  Request $request    [description]
     * @param  [type]  $company_id [description]
     * @return [type]              [description]
     */
    public function chat(Request $request, $company_id)
    {
    	if($request->session()->has('loginuser')){
            $user = $request->session()->get("loginuser");
            $company = \DB::table('company')->where('company_id',$company_id)->first();
    		$company_all = \DB::table('company')->get();
            $user_all = \DB::table('user')->get();
            return view('home.chat',compact('user','company','company_all'));
        }else{
        	return redirect('/login');
        }
    }
}

公司列表前端页面:

<html><head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>房间列表</title>
    <style type="text/css">
    .center{ width:800px; height:100%; background:#F5FAFF; margin:0 auto;}
    .room{ width:200px; height:100px; border:1px solid red; text-align:center; line-height:100px; float:left; margin:5px; }
    a{ color:green; }
    </style>
</head>
<body>
    <div class="center">
        @foreach($res as $key => $val)
        <a href="/chat/{{$val->company_id}}" target="_blank">
            <div class="room">
                {{$val->company_name}}
            </div>
        </a>
        @endforeach
    </div>
</body>
</html>

====================核心代码=================

聊天页面直接用的workerman聊天室的demo:https://www.workerman.net/workerman-chat

<html><head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>{{$company->company_name}}PHP聊天室</title>
    <link href="{{asset('chat/css/bootstrap.min.css')}}" rel="stylesheet">
    <link href="{{asset('chat/css/jquery-sinaEmotion-2.1.0.min.css')}}" rel="stylesheet">
    <link href="{{asset('chat/css/style.css')}}" rel="stylesheet">
  
    <script type="text/javascript" src="{{asset('chat/js/swfobject.js')}}"></script>
    <script type="text/javascript" src="{{asset('chat/js/web_socket.js')}}"></script>
    <script type="text/javascript" src="{{asset('chat/js/jquery.min.js')}}"></script>
    <script type="text/javascript" src="{{asset('chat/js/jquery-sinaEmotion-2.1.0.min.js')}}"></script>

    <script type="text/javascript">
    if (typeof console == "undefined") {    this.console = { log: function (msg) {  } };}
    // 如果浏览器不支持websocket,会使用这个flash自动模拟websocket协议,此过程对开发者透明
    WEB_SOCKET_SWF_LOCATION = "{{asset('chat/swf/WebSocketMain.swf')}}";
    // 开启flash的websocket debug
    WEB_SOCKET_DEBUG = true;
    var ws, name, client_list={};

    // 连接服务端
    function connect() {
       // 创建websocket
        ws = new WebSocket("ws://"+document.domain+":2346");
       // 当socket连接打开时,输入用户名
        ws.onopen = onopen;
       // 当有消息时根据消息类型显示不同信息
        ws.onmessage = onmessage; 
        ws.onclose = function() {
            console.log("连接关闭,定时重连");
            connect();
        };
        ws.onerror = function() {
            console.log("出现错误");
        };
    }

    // 连接建立时发送登录信息
    function onopen()
    {
        if(!name)
        {
            show_prompt();
        }
        // 登录
        var login_data = '{"type":"login","client_name":"'+name.replace(/"/g, '\\"')+'","room_id":"'+'{{$company->company_id}}'+'"}';
        console.log("websocket握手成功,发送登录数据:"+login_data);
        ws.send(login_data);
    }

    // 服务端发来消息时
    function onmessage(e)
    {
        console.log(e.data);
        var data = JSON.parse(e.data);
        switch(data['type']){
            // 服务端ping客户端
            case 'ping':
                ws.send('{"type":"pong"}');
                break;;
            // 登录 更新用户列表
            case 'login':
                //{"type":"login","client_id":xxx,"client_name":"xxx","client_list":"[...]","time":"xxx"}
                say(data['client_id'], data['client_name'],  data['client_name']+' 加入了聊天室', data['time']);
                if(data['client_list'])
                {
                    client_list = data['client_list'];
                }
                else
                {
                    client_list[data['client_id']] = data['client_name']; 
                }
                flush_client_list();
                console.log(data['client_name']+"登录成功");
                break;
            // 发言
            case 'say':
                //{"type":"say","from_client_id":xxx,"to_client_id":"all/client_id","content":"xxx","time":"xxx"}
                say(data['from_client_id'], data['from_client_name'], data['content'], data['time']);
                break;
            // 用户退出 更新用户列表
            case 'logout':
                //{"type":"logout","client_id":xxx,"time":"xxx"}
                say(data['from_client_id'], data['from_client_name'], data['from_client_name']+' 退出了', data['time']);
                delete client_list[data['from_client_id']];
                flush_client_list();
        }
    }

    // 输入姓名
    function show_prompt(){
        // name = prompt('输入你的名字:', '');
        name = "{{$user->name}}"
        if(!name || name=='null'){  
            name = '游客';
        }
    }  

    // 提交对话
    function onSubmit() {
        var input = document.getElementById("textarea");
        var to_client_id = $("#client_list option:selected").attr("value");
        var to_client_name = $("#client_list option:selected").text();
        ws.send('{"type":"say","to_client_id":"'+to_client_id+'","to_client_name":"'+to_client_name+'","content":"'+input.value.replace(/"/g, '\\"').replace(/\n/g,'\\n').replace(/\r/g, '\\r')+'"}');
        input.value = "";
        input.focus();
    }

    // 刷新用户列表框
    function flush_client_list(){
        var userlist_window = $("#userlist");
        var client_list_slelect = $("#client_list");
        userlist_window.empty();
        client_list_slelect.empty();
        userlist_window.append('<h4>在线用户</h4><ul>');
        client_list_slelect.append('<option value="all" id="cli_all">所有人</option>');
        for(var p in client_list){
            userlist_window.append('<li id="'+p+'">'+client_list[p]+'</li>');
            client_list_slelect.append('<option value="'+p+'">'+client_list[p]+'</option>');
        }
        $("#client_list").val(select_client_id);
        userlist_window.append('</ul>');
    }

    // 发言
    function say(from_client_id, from_client_name, content, time){
        //解析新浪微博图片
        content = content.replace(/(http|https):\/\/[\w]+.sinaimg.cn[\S]+(jpg|png|gif)/gi, function(img){
            return "<a target='_blank' href='"+img+"'>"+"<img src='"+img+"'>"+"</a>";}
        );

        //解析url
        content = content.replace(/(http|https):\/\/[\S]+/gi, function(url){
            if(url.indexOf(".sinaimg.cn/") < 0)
                return "<a target='_blank' href='"+url+"'>"+url+"</a>";
            else
                return url;
        }
        );

        $("#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(){
        select_client_id = 'all';
        $("#client_list").change(function(){
             select_client_id = $("#client_list option:selected").attr("value");
        });
        $('.face').click(function(event){
            $(this).sinaEmotion();
            event.stopPropagation();
        });
    });
    </script>
</head>
<body onload="connect();">
    <div class="container">
        <div class="row clearfix">
            <div class="col-md-1 column">
            </div>
            <div class="col-md-6 column">
                <div class="thumbnail">
                    <div class="caption" id="dialog"></div>
                </div>
                <form onsubmit="onSubmit(); return false;">
                    <select style="margin-bottom:8px" id="client_list">
                        <option value="all">所有人</option>
                    </select>
                    <textarea class="textarea thumbnail" id="textarea"></textarea>
                    <div class="say-btn">
                        <input type="button" class="btn btn-default face pull-left" value="表情" />
                        <input type="submit" class="btn btn-default" value="发表" />
                    </div>
                </form>
                <div>
                    &nbsp;&nbsp;&nbsp;&nbsp;<b>房间列表:</b>(当前在&nbsp;房间{{$company->company_id}})<br>
                    @foreach($company_all as $key => $val)
                    &nbsp;&nbsp;&nbsp;&nbsp;<a href="/chat/{{$val->company_id}}">房间{{$val->company_id}}</a>
                    @endforeach
                    <br><br>
                </div>
            </div>
            <div class="col-md-3 column">
                <div class="thumbnail">
                    <div class="caption" id="userlist"></div>
                </div>
            </div>
        </div>
    </div>
    <script type="text/javascript">
        // 动态自适应屏幕
        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>
</body>
</html>

创建workerman文件:

php artisan make:command WorkermanCommand

WorkermanCommand.php中:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use GatewayWorker\BusinessWorker;
use GatewayWorker\Gateway;
use GatewayWorker\Register;
use Workerman\Worker;

class WorkermanCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'workerman {action} {--d}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'workerman';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        global $argv;
        $action = $this->argument('action');

        $argv[0] = 'wk';
        $argv[1] = $action;
        $argv[2] = $this->option('d') ? '-d' : '';

        $this->start();
    }


    private function start()
    {
        $this->startGateWay();
        $this->startBusinessWorker();
        $this->startRegister();
        Worker::runAll();
    }

    private function startBusinessWorker()
    {
        $worker                  = new BusinessWorker();
        $worker->name            = 'BusinessWorker';
        $worker->count           = 1;
        $worker->registerAddress = '127.0.0.1:1236';
        $worker->eventHandler    = \App\Events\Workerman::class;
    }

    private function startGateWay()
    {
        $gateway = new Gateway("websocket://0.0.0.0:2346");
        $gateway->name                 = 'Gateway';
        $gateway->count                = 1;
        $gateway->lanIp                = '127.0.0.1';
        $gateway->startPort            = 2300;
        $gateway->pingInterval         = 30;
        $gateway->pingNotResponseLimit = 0;
        $gateway->pingData             = '{"type":"@heart@"}';
        $gateway->registerAddress      = '127.0.0.1:1236';
    }

    private function startRegister()
    {
        new Register('text://0.0.0.0:1236');
    }
}

记得把命令文件添加进Kernel.php的$commands中:

protected $commands = [
    \App\Console\Commands\WorkermanCommand::class
];

创建事件监听文件:

php artisan make:event Workerman

Workerman.php文件中放wokerman-chat的demo中的Applications/Chat/Events.php的内容

<?php

namespace App\Events;

use GatewayWorker\Lib\Gateway;

class Workerman
{
    /**
    * 有消息时
    * @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'));
                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));
        }
    }
}

运行workerman

php artisan workerman start

进入聊天室:

 

项目地址:https://github.com/lingdulvlv/laravel_workerman

 

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐