Project Background:

Recently, due to the business requirements of the company, we need to use the chat function. And there are more personalized needs to be customized. Previously using other people’s chat components was based on microengines. If you want to migrate to ordinary H5, there are more difficulties in logic modification and customization. So you have to get over the hump and build your own


What is a Workerman?

Workerman is an open-source, high-performance asynchronous PHP socket instant messaging framework. It supports high concurrency and ultra-high stability, and is widely used in the development of mobile app, mobile communication, wechat small program, mobile game server, online games, PHP chat room, hardware communication, smart home, Internet of cars, Internet of things and other fields. Supports TCP long connections, Websocket, HTTP, and custom protocols. With asynchronous Mysql, asynchronous Redis, asynchronous Http, MQTT Internet of Things client, asynchronous message queue and many other high-performance components.

Let’s get real!

1. The first step is to write down the extension Composer for workerman

"workerman/gateway-worker": "^ 3.0"."workerman/gatewayclient": "^ 3.0"."workerman/workerman": "^ 3.5".Copy the code

2. The second step is to download the demo from the official website and put it in the directory of our project


In the picture I’ll put the whole project in the HTTP/Controller/Workerman.

3. In the third step, we need to modify the reference part of the following three files to the following. Otherwise, a path error is reported

Start_businessworker, start_gateway, start_register

require_once __DIR__ . '/.. /.. /.. /.. /.. /vendor/autoload.php';Copy the code

4. After modification, we can directly run the corresponding startup file in Liunx

php start.php start -dIf you are in window, double-click start_for_win.bat to run itCopy the code

5. After successful operation, you should see the following screen



At this point, we have built a communication environment based on Workerman. Then we can develop according to our own project requirements. Here is a key explanation to you. All of our chat logic is modified in the directory events.php.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — luxuriant line — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — –

I’ll post some of the code I wrote for you below.

Event.php

<? 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<[email protected]> * @copyright walkor<[email protected]> * @link http://www.workerman.net/ * @license http://www.opensource.org/licenses/mit-license.php MIT License * / / * * * business code used in the detection of infinite loop or long time blocking issues such as business card * if found dead, can be the followingdeclareOpen (remove // comments) and execute PHP start. PHP reload * then watch workerman.log for a while to see if process_timeout is abnormal */ //declare(ticks=1); /** * onMessage onClose */ use \GatewayWorker\Lib\Gateway; Class Events {/** * by: @param * when the client is connected * created time: 2018/10/25 * @param$client_idThis ID is automatically generated for the gatewayworker ID */ public staticfunction onConnect($client_id)
    {
        Gateway::sendToClient($client_id, json_encode(array(
            'type'= >'init'.'client_id'= >$client_id))); } /** * @param int when there is a message$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"; // The client passes json data$message_data = json_decode($message.true);
        if(!$message_data)
        {
            return; } // Execute different service switches according to the type ($message_data['type']) {// Heartbeat between client and servercase 'pong':
                return; // Client login message format: {type:login, name:xx, room_id:1}, add to the client, broadcast to all clients xx enter the chat roomcase 'login'Check if there is a room numberif(! isset($message_data['room_id']))
                {
                    throw new \Exception("\$message_data['room_id'] not set. client_ip:{$_SERVER['REMOTE_ADDR']} \$message:$message"); } // Put the room nickname into session$room_id = $message_data['room_id'];
                $client_name = htmlspecialchars($message_data['client_name']);
                $_SESSION['room_id'] = $room_id;
                $_SESSION['client_name'] = $client_name; // Get a list of all users in the room$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; // Relay to all clients in the current room, xx enters the chat room 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); // Send a list of users to the current user$new_message['client_list'] = $clients_list;
                Gateway::sendToCurrentClient(json_encode($new_message));
                return; // message: {type:say, to_client_id:xx, content:xx}
            case 'say': // Invalid requestif(! 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']; / / / / private chatif($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'= >" says to you: ".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 > for you".htmlspecialchars($message_data['to_client_name'])."Say: < / 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)); }} /** ** when the client is disconnected * @paraminteger $client_idClient ID */ public staticfunction 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"; // Delete from the client list of the roomif(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)); }}}Copy the code

Client page

<! DOCTYPE html> <html lang="en">
<head>
    <meta charset="UTF-8"With {{> < title >$to->name}} dialog </title> <scripttype="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="stylesheet">
    <link href="{{asset('css')}}/bootstrap.min.css" rel="stylesheet">
    <link href="{{asset('css')}}/style.css" 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.5 px; } </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;"> {{-- dialog header --}} <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 > {{- / / dialogue window contents -}} < div class ="content" style="width: 500px; height: 400px; border: 1px solid green; margin-top: 40px; overflow-y: auto"> <div style= "max-width: 100%; clear: both; min-height: 1em"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 > --}} {{- my head with words -}} {{- < 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 > {{- send dialogue window -}} < 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="Expression" />
                <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) { } }; } // If the browser does not support websocket, the flash will automatically emulate the WebSocket protocol, which is transparent to the developer"/swf/WebSocketMain.swf"; // Enable flash websocket debug WEB_SOCKET_DEBUG =true;
    var ws, name, client_list={};
    var to_client_id=""; // Connect server initialization functionfunction connect() {// create websocket can then be replaced with the corresponding server address ws = new websocket ()"ws://"+document.domain+": 7272"); // When the socket connection is opened, enter the username ws. Onopen = onopen; Ws. onMessage = onMessage; ws. onMessage = onMessage; // When a connection is lost, call the connection method to try to reconnect with ws-onclose =function() {
            console.log("Connection closed. Timed reconnection."); connect(); }; // If the operation fails, return exception ws.onError =function() {
            console.log("Error occurred"); }; $.post($.post)"/get_record", { "room":"{{$room}}" },
            function(msg){
                $.each(msg,function(v,k) { console.log(k); / / determineif(k.tag! ="{{$tag}}"){
                        $(".content").append(
                            '
      
'
+ '<div style="width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: left">'+ '<img src="{{$to->heading}}" width="50px" height="50px">'+ '</div>'+ '
'
+k.content+'</div>'+ '<div>' ).parseEmotion(); }else{$(".content").append( '
'
+ '<div style="width: 50px;height: 50px; border: 1px solid red; margin-left:10px; float: right">'+ '<img src="{{$from->heading}}" width="50px" height="50px">'+ '</div>'+ '
'
+k.content+'</div>'+ '<div>').parseEmotion(); }})}); } // Send login information when the connection is establishedfunction 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('Login successful'} // When the server sends a messagefunction onmessage(e) { var data = JSON.parse(e.data); switch(data['type']){// The server ping the heartbeat of the clientcase 'ping': ws.send('{"type":"pong"}'); break; // Log in to update the user listcase 'login': // Save the required send ID to the local to_client_id variablefor(var p in data['client_list']){ to_client_id=p; } console.log(to_client_id); break; / / to speakcase 'say': console.log(data); say(data['from_client_id'], data['from_client_name'], data['content'], data['time']); break; // The user exits to update the user listcase 'logout': console.log(data); break; case 'init'// Here you can send Ajax to bind different user ids and client console.log(data);break; }} // Submit the dialogfunction onSubmit() {// Check whether there are more than 20 records in the current sessiontrue; $. Ajax ({url:"/check_count".type: "post", async:false, // cache: false, // contentType: false, // processData: false, data:{ 'room':"1", }, success: function (msg) { if(msg>10){ alert('The current conversation has exceeded the number of times, please purchase the corresponding service') count=false; }}});if(count){ var neirong=$("#textarea").val().replace(/"/g, '\\"').replace(/\n/g,'\\n').replace(/\r/g, '\\r'); 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, 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(){// select_client_id = 'all'; $("#client_list"). Change (function(){select_client_id = $("#client_list") option:selected").attr("value"); }); // select $(').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) {if(e.keycode === 13&&! e.ctrlKey) { e.preventDefault(); $('form').submit(); return false; If (e.keyCode === 13&&e.trlkey){$(this). Val (function(I,val){return val + "\n"; }); }}); Copy the code


These two code snippets are essentially the core snippets of the main run. The parameters of other frameworks need to be adjusted and optimized according to the documentation. The Workerman-based chat function demo has been built.