laravel+GatewayWorker 完成IM即时通信以及文件互传功能(第五章:前后端代码开发+演示)

功能简介

本专题将带手把手带你搭建 仿某信的 即时通信功能 并完成文件互传

应用场景

本专题实际应用场景:

  1. 聊天客服:即时通信,消息实时互传,互相发送文字、语音消息以及文件;
  2. 小规模线上竞拍;
  3. 视频实时弹幕;
  4. 物联网;
  5. 以及其他与实时消息相关的功能;

专题章节

  1. 服务器配置
  2. 业务逻辑讲解
  3. 后端即时通信代码开发与配置项讲解
  4. 服务端调试错误
  5. 前后端代码开发
  6. 功能展示

第四章:前端代码开发

一、所需页面

  • 用户登录页面(注册页面:用户存在就登录,不能存在就自动注册)
  • 用户好友列表页面
  • 添加好友页面
  • 用户一对一聊天页面

二、业务逻辑

  • 用户登录→进入好友列表页面→添加好友→点击选择好友→进入好友一对一聊天页面→开始聊天→结束聊天End;

三、前端代码编写

相信大家的前端能力都不错,我在这里就不着重写css样式了,重点为大家演示功能实现
在这里插入图片描述

  • 代码在下面,又臭又长估计你也不爱看;我简单说下前端逻辑;
  1. 用户进入页面直接连socket,连上了后端会给你返回client_id用户的临时设备唯一id
  2. 用户拿着后端给的临时设备id发ajax到后端进行我方用户id与临时设备id的绑定;
  3. 绑定好了就可以进行消息发送了
  4. 字符串发送接直接把内容和收件人id发送到后端,后端自行调用GatewayWorker发送消息;
  5. 发送文件和发送文字消息一样,文件的本质其实也是一堆字符串,前端把文件转base64成一堆字符串发给后端,后端当字符串在给你转发出去,等到客户端接受到base64后直接还原成文件就好了;
  • 前端核心代码
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="{{asset('layui-v2.6.8/layui/css/layui.css')}}">
    <script src="{{asset('js/jquery.min.js')}}"></script>
    <script src="{{asset('layui-v2.6.8/layui/layui.js')}}"></script>
    <link rel="stylesheet" href="{{asset('css/im_chat.css')}}">
    <title>聊天页面</title>
</head>
<body>
<fieldset class="layui-elem-field layui-field-title" style="margin-top: 20px;">
    <legend>聊天页面</legend>
</fieldset>

<main>
    <section class="message_box">
        {{--
        <div class="left">
            <img class="header_img" src="{{asset('images/linux.png')}}" alt="">
            <div class="content_left">
                <p>对方方对方</p>
            </div>
        </div>
        <div class="right">
            <div class="content_right">
                <p>我方我方</p>
            </div>
            <img class="header_img" src="{{asset('images/php.png')}}" alt="">
        </div>
        <div class="left">
            <img class="header_img" src="{{asset('images/linux.png')}}" alt="">
            <div class="content_left">
                <img src="{{asset('images/demo1.jpg')}}" alt="">
            </div>
        </div>
        <div class="right">
            <div class="content_right">
                <img src="{{asset('images/demo1.jpg')}}" alt="">
            </div>
            <img class="header_img" src="{{asset('images/php.png')}}" alt="">
        </div>
        --}}

    </section>


</main>

<section class="import_box">
    <div id="string">
        <a href="javascript:;" onclick="transitionToggle()">+</a>
        <textarea name="" id="string_msg_val" cols="30" rows="5" required placeholder="请输入聊天内容"></textarea>
        <button type="button" onclick="sendStringMsg()">发送</button>
    </div>

    <div id="file_box" style="display: none">
        <a href="javascript:;"  onclick="transitionToggle()"></a>
        <input type="file" id="file_input" onchange="filechange('file_input')">
        <input type="hidden" id="base64_value">
        <button type="button" onclick="sendImgMsg()">发送</button>
    </div>

</section>

</body>
<script>
    /**
     * 与GatewayWorker建立websocket连接,域名和端口改为你实际的域名端口,
     * 其中端口为Gateway端口,即start_gateway.php指定的端口。
     * start_gateway.php 中需要指定websocket协议,像这样
     * $gateway = new Gateway(websocket://0.0.0.0:8282);
     */
    ws = new WebSocket("ws://im.liutong.pro:8282");//这里的端口要与.\app\GatewayWorker\Applications\YourApp\start_gateway.php中$gateway = new Gateway("websocket://0.0.0.0:8282");的端口保持一致;

    //检测websocket是否链接
    ws.onopen = function () {
        console.log("连接成功");
    };

    // 关闭 websocket
    ws.onclose = function () {
        console.log("连接已关闭...");
    };

    // 服务端主动推送消息时会触发这里的onmessage
    ws.onmessage = function(e){
        // json数据转换成js对象
        var data = eval("("+e.data+")");
        var type = data.type || '';
        console.log(type);
        switch(type){
            // Events.php中返回的init类型的消息,将client_id发给后台进行uid绑定
            case 'init'://监听初始化
                console.log(data);
                // 利用jquery发起ajax请求,将client_id发给后端进行uid绑定
                binding(data.client_id);
                break;
            case 'binding_success'://监听绑定成功信息
                console.log(data);
                break;
            case 'string'://监听收到的文字信息
                console.log(data);
                if(data.addresser_id != "{{$friend_id}}"){
                    return false;
                }
                //将文字信息渲染到视图上
                let _html = "<div class=\"left\">\n" +
                "            <img class=\"header_img\" src=\"{{asset('images/linux.png')}}\" alt=\"\">\n" +
                "            <div class=\"content_left\">\n" +
                "                <p>"+ data.msg +"</p>\n" +
                "            </div>\n" +
                "        </div>";
                $('.message_box').append(_html);//将新的文字信息追加到模板上
                break;
            case 'img'://监听收到的图片信息
                console.log(data);
                if(data.addresser_id != "{{$friend_id}}"){
                    return false;
                }
                let _img_html = "<div class=\"left\">\n" +
                    "            <img class=\"header_img\" src=\"{{asset('images/linux.png')}}\" alt=\"\">\n" +
                    "            <div class=\"content_left\">\n" +
                    "                <img src=" + data.msg + ">\n" +
                    "            </div>\n" +
                    "        </div>";
                $('.message_box').append(_img_html);//将新的文字信息追加到模板上
                break;
            default :
                alert(e.data);
        }
    };

    //切换输入框
    function transitionToggle(){
        $('#string').toggle();
        $('#file_box').toggle();
    }

    //绑定uid
    function binding(client_id){
        //绑定业务逻辑
        $.ajax({
            type : "POST", //提交方式
            url : "{{route('im.binding')}}",//路径
            data : {
                '_token':'{{csrf_token()}}'//laravel 防止ajax POST请求报419错误方法
                ,'user_id' : "{{session('user')['id']}}"
                ,'client_id' :client_id
            },//数据,这里使用的是Json格式进行传输
            success : function(result) {//返回数据根据结果进行相应的处理
                var data = JSON.parse(result);//接受到服务器给我返回的JSON数据并进行解析
                if (data.success) {
                    console.log('绑定结果:' + data.msg);
                } else {
                    alert('绑定失败');
                }
            }
        });

    }

    //发送文字消息
    function sendStringMsg() {
        let msg = $('#string_msg_val').val();
        if(!msg){
            layer.msg('请输入内容', {time: 5000, icon: 2});
            return false;
        }
        $.ajax({
            type : "POST", //提交方式
            url : "{{route('im.sendMessage')}}",//路径
            data : {
                '_token':'{{csrf_token()}}'//laravel 防止ajax POST请求报419错误方法
                ,'recipients_id' :"{{$friend_id}}"//好友id
                ,'type':'string'
                ,'msg':msg
            },//数据,这里使用的是Json格式进行传输
            success : function(result) {//返回数据根据结果进行相应的处理
                var data = JSON.parse(result);//接受到服务器给我返回的JSON数据并进行解析
                if (data.success) {
                    console.log('文字发送结果:' + data.msg);
                    //将发送的信息显示到html中
                    let _string = " <div class=\"right\">\n" +
                        "            <div class=\"content_right\">\n" +
                        "                <p>"+ data.msg +"</p>\n" +
                        "            </div>\n" +
                        "            <img class=\"header_img\" src=\"{{asset('images/php.png')}}\" alt=\"\">\n" +
                        "        </div>";
                    $('.message_box').append(_string);//将新的文字信息追加到模板上
                    $('#string_msg_val').val('');//清理输入框
                } else {
                    layer.msg('发送失败', {time: 5000, icon: 2});
                }
            }
        });
    }

    //发送图片文件
    function sendImgMsg(){
        let msg = $('#base64_value').val();//获取base64发送给php接口
        $.ajax({
            type : "POST", //提交方式
            url : "{{route('im.sendMessage')}}",//路径
            data : {
                '_token':'{{csrf_token()}}'//laravel 防止ajax POST请求报419错误方法
                ,'recipients_id' :"{{$friend_id}}"//好友id
                ,'type':'img'
                ,'msg':msg
            },//数据,这里使用的是Json格式进行传输
            success : function(result) {//返回数据根据结果进行相应的处理
                var data = JSON.parse(result);//接受到服务器给我返回的JSON数据并进行解析
                if (data.success) {
                    console.log('文字发送结果:' + data.msg);
                    //将发送的信息显示到html中
                    let _string = "<div class=\"right\">\n" +
                        "            <div class=\"content_right\">\n" +
                        "                <img src="+data.msg+" alt=\"\">\n" +
                        "            </div>\n" +
                        "            <img class=\"header_img\" src=\"{{asset('images/php.png')}}\" alt=\"\">\n" +
                        "        </div>";
                    $('.message_box').append(_string);//将新的文字信息追加到模板上
                    $('#string_msg_val').val('');//清理输入框
                } else {
                    layer.msg('发送失败', {time: 5000, icon: 2});
                }
            }
        });
    }

    /**
     * 获取文件base64
     */
    function filechange(_id_name) {
        var selectFile = document.getElementById(_id_name);
        var filePath = selectFile.value;//文件路径
        var fileName = selectFile.files[0].name;//上传的文件名称
        var file = selectFile.files[0];//上传的文件
        var extn = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();//文件后缀
        //文件base64string获取
        if (window.FileReader) {
            var reader = new FileReader();
            reader.readAsDataURL(file);
            //监听文件读取结束后事件
            reader.onloadend = function (e) {
                var base64String=e.target.result;
                $('#base64_value').val(base64String);
            };
        }
    }
</script>

</html>
  • 路由文件routes\web.php
<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});


//加载登录页面
Route::get('login','LoginController@index');
//登录+注册处理
Route::post('login/loginOperation','LoginController@loginOperation')->name('login.loginOperation');


//需要中间件验证的路由
Route::middleware(['im_auth'])->group(function () {
    //退出登录
    Route::post('login/loginOut','LoginController@loginOut')->name('login.loginOut');

    //加载im用户好友列表
    Route::get('im/indexList', 'ImController@indexList')->name('im.indexList');
    //添加好友
    Route::post('im/addFriends', 'ImController@addFriends')->name('im.addFriends');
    //删除好友
    Route::post('im/deleteFriends/{friend_lists_id}', 'ImController@deleteFriends')->name('im.deleteFriends');
    //加载im用户聊天页面
    Route::get('im/chat/{friend_id}', 'ImController@chat')->name('im.chat');

    //绑定用户
    Route::post('im/binding', 'ImController@binding')->name('im.binding');
    //发送消息
    Route::post('im/sendMessage', 'ImController@sendMessage')->name('im.sendMessage');
});
  • 数据库表结构文件
    1.im用户表 database/migrations/2021_11_29_093718_create_im_users_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateImUsersTable extends Migration
{
    /**
     * im用户信息表
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('im_users', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->char('tel',15)->unique()->comment('手机号|唯一索引');
            $table->string('password');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('im_users');
    }
}

2.好友关系表 database/migrations/2021_11_30_033101_create_friend_lists_table.php

<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateFriendListsTable extends Migration
{
    /**
     * 好友列表
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('friend_lists', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->timestamps();
            $table->integer('user_id')->comment('用户id');
            $table->integer('friend_id')->comment('好友id');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('friend_lists');
    }
}
  • 后端业务逻辑控制器
<?php

namespace App\Http\Controllers;

// 自动加载类
require_once __DIR__ .'/../../GatewayWorker/vendor/autoload.php';//引入绝对路径上的GatewayWorker组件上的自动加载类,不然回报找不到"GatewayClient\Gateway"的错误,或者你也可以在项目更目录composer安装"GatewayClient\Gateway"这个

use App\FriendList;
use App\ImUser;
use GatewayClient\Gateway;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

/**
 * Im控制器
 * Class ImController
 * @package App\Http\Controllers
 */
class ImController extends Controller
{
    /**
     * 好友列表
     */
    public function indexList()
    {
        $user = session('user');
        $data = FriendList::where('user_id', $user['id'])->paginate(20);
        return view('im.indexList', compact('data'));
    }

    /**
     * 添加好友
     * @param Request $request
     */
    public function addFriends(Request $request)
    {
        $friend_id = ImUser::where('tel', $request->tel)->value('id');
        $user = session('user');
        if ($friend_id) {
            //对方id存在
            $is_friend = FriendList::where([
                ['user_id', $user['id']],
                ['friend_id', $friend_id],
            ])->exists();
            if(!$is_friend){
                FriendList::create([
                    'user_id'=>$user['id'],
                    'friend_id'=>$friend_id,
                ]);
                FriendList::create([
                    'user_id'=>$friend_id,
                    'friend_id'=>$user['id'],
                ]);
                return redirect()->route('im.indexList');
            }
        }
        dd('对方手机号有误');
    }

    /**
     * 删除好友
     * @param $id
     */
    public function deleteFriends($friend_lists_id){
        $friendList = FriendList::where('id',$friend_lists_id)->first();

        DB::beginTransaction();
        try{
            $del_1 = FriendList::where([
                ['user_id',$friendList['user_id']],
                ['friend_id',$friendList['friend_id']]
            ])->delete();
            $del_2 = FriendList::where([
                ['user_id',$friendList['friend_id']],
                ['friend_id',$friendList['user_id']]
            ])->delete();
            DB::commit();
        }catch (\Exception $exception){
            DB::rollBack();
            dd('删除失败:' . $exception->getMessage());
        }

        if($del_1 && $del_2){
            return redirect()->back();
        }
        dd('删除失败');
    }

    /**
     * 聊天页面
     * @param Request $request
     * @param $friend_id 好友id
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function chat(Request $request, $friend_id)
    {
        return view('im.chat',compact('friend_id'));
    }

    /**
     * 绑定用户
     * @param Request $request
     */
    public function binding(Request $request){
        $client_id = $request->client_id;//GatewayWorker 给我们分配的连接id
        $user = session('user');
        $uid  = $user['id'];//我在我方数据库IM用户表中的主键id
        Gateway::bindUid($client_id,$uid);
        $message = [
            'type' => 'binding_success',
            'msg' => '绑定成功'
        ];
        Gateway::$registerAddress = '127.0.0.1:1238';
        Gateway::sendToUid($uid, json_encode($message));//向任意uid的网站页面发送数据
        $message['code'] = 200;
        $message['success'] = 1;//1:成功,0:失败
        return json_encode($message);
    }

    /**
     * 发送消息
     * @param Request $request->recipients_id 收件人id,在我方friend_lists好友表中的friend_id字段值
     * @param Request $request->type 信息类型 string:文字信息,img:图片
     * @param Request $request->msg 信息内容
     */
    public function sendMessage(Request $request){
        $post = $request->all();
        $user = session('user');
        $user_id = $user['id'];
        $message = [
            'type' => $request->type,
            'addresser_id' => $user_id,//发件人id
            'recipients_id' => $post['recipients_id'],//收件人id
            'msg' => $post['msg'],
        ];
        //检测是不是文件,是的话给文件一个随机生成的文件id,方便前端操作
        switch ($request->type){
            case 'img':
                $message['file_id'] = time() . rand(10,99);//文件id
                break;
            default:
                $message['file_id'] = '';
                break;
        }
        Gateway::$registerAddress = '127.0.0.1:1238';
        Gateway::sendToUid($post['recipients_id'], json_encode($message));//给发件人的uid网站页面发送数据
        $message['code'] = 200;
        $message['success'] = 1;//1:成功,0:失败
        return json_encode($message);
    }
}

注意:后端业逻辑控制器要是报错说:"找不到GatewayClient\Gateway的错误"记得要在php代码顶部使用require_once 进行引入;
要是引入了还是找不到:那一定是 项目根目录\app\GatewayWorker里面没有composer require workerman/gatewayclient 切仅目录安装已就好了;

代码我传到gitee(码云)上面啦!!!
https://gitee.com/liutong199309/php-laravel–gateway-worker

上一章:《4.服务端调试错误

已完结;

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值