Maison > cadre php > Workerman > Comment mettre en œuvre le chat en ligne à l'aide de Workerman

Comment mettre en œuvre le chat en ligne à l'aide de Workerman

Libérer: 2019-12-26 17:00:53
avant
3403 Les gens l'ont consulté

Comment mettre en œuvre le chat en ligne à l'aide de Workerman

workerman est un service de communication écrit en PHP. Les projets précédents l'ont utilisé pour les services d'interface de données

Cette fois, je l'ai utilisé pour créer un simple salon de discussion en ligne~

1 Téléchargez la dernière version de Workerman

Vous pouvez y aller. http://www.workerman.net Allez télécharger

J'ai séparé le service et le client en deux dossiers pour faciliter la gestion

La structure générale du projet est la suivante.

Comment mettre en œuvre le chat en ligne à laide de Workerman

Client :

Le client est simple. Un simple code HTML. Intégré à un service d'écoute de 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("出现错误");
       };
    }
Copier après la connexion

pour réaliser l'ouverture de websocket, la surveillance des messages et la fermeture

1 Lorsqu'un client est ouvert, une boîte de dialogue pour saisir un nom apparaîtra immédiatement <🎜. >

function onopen(){
        //console.log(name);
        //var username=connect_id="";
        if(!name)
        {
            name=prompt("请输入您的名字","");
            if(!name || name==&#39;null&#39;){ 
                name = &#39;咕哒子&#39;;
            }
        }
 
        $(&#39;#curuser&#39;).text(name);
 
         data=&#39;{"type":"1","user":"&#39;+name+&#39;"}&#39;;
         
        ws.send(data);
    }
Copier après la connexion

et envoyez les données au serveur. type =1 représente la connexion.

2. Lors de la réception d'un message, déterminez le type de message, s'il s'agit d'un message de groupe ou d'un message privé. Et puis traitez.

De plus, chaque fois qu'un nouvel utilisateur se connecte, une liste d'utilisateurs sera transmise à chaque client. Rendu

function onmessage(e){
        //console.log(e.data);
        var data = eval("("+e.data+")");
        var info=$(&#39;#chatinfo&#39;).html();
        if(data.type==1)
            $(&#39;#chatinfo&#39;).html(info+&#39;<br/>&#39;+data.data);
        else if(data.type==2)
        {
            // 在线用户列表 userinfo
            $(&#39;#userinfo&#39;).html(data.data);
        }
        else if(data.type==3)
        {
            // 在线用户列表 个人信息
            name=data.data.userinfo;
            //console.log(data.data);
        }
    }
Copier après la connexion

et ensuite il y a le code permettant à chaque utilisateur d'envoyer des messages. Il peut s'agir d'un chat privé ou d'un message de groupe

$(&#39;#send&#39;).click(function(e){
        var msg=$(&#39;#msg&#39;).val();
        var tofriend=$(&#39;#tofriend&#39;).val();
        var tofriendname=$(&#39;#tofriendname&#39;).val();
        if(tofriend!="")
        {
            data=&#39;{"type":"3","user":"&#39;+name+&#39;","msg":"&#39;+msg+&#39;","friend_id":"&#39;+tofriend+&#39;","friendname":"&#39;+tofriendname+&#39;"}&#39;;
        }else{
            data=&#39;{"type":"2","user":"&#39;+name+&#39;","msg":"&#39;+msg+&#39;"}&#39;;
        }
        ws.send(data);
        $(&#39;#msg&#39;).attr("value",&#39;&#39;);
    });
Copier après la connexion

C'est à peu près tout pour le client.

Le client a plusieurs écueils,

Pit 1. Si le nom de la variable est name, elle ne sera pas réinitialisée lors du rafraîchissement de la page web, sinon elle sera réinitialisée. (Après avoir vérifié les informations, j'ai découvert que la variable name est window.name. Par conséquent, la valeur ne sera pas actualisée lors de l'actualisation de la page Web)

Pit 2. Pour le tableau de groupe js, la variable doit utiliser " " et la couche la plus externe est '' Par exemple : data='{"type":"1","user":"'+name+'"}'; Sinon, il y aura des problèmes d'analyse. Je ne peux pas faire l'inverse !

Côté serveur :

Le côté serveur est principalement le composant de travail et utilise le composant de communication distribuée Channel pour mettre en œuvre l'abonnement, le push de cluster, le push de groupe et le chat privé.

Le premier, bien sûr, est la surveillance, qui permet la surveillance du websocket d'un travailleur

// 创建一个Worker监听2346端口,使用websocket协议通讯
$ws_worker = new Worker("websocket://0.0.0.0:2345");
$channel_server = new Channel\Server(&#39;0.0.0.0&#39;, 2206);
// 启动4个进程对外提供服务
$ws_worker->count = 4;
$ws_worker->name="kinmoschat";
Copier après la connexion

Lorsque la surveillance des travailleurs est activée, enregistrez le canal de communication.

$ws_worker->onWorkerStart=function($ws_worker)
{
    // channel 客户端链接上 服务器
    Channel\Client::connect(&#39;127.0.0.1&#39;,2206);
    $event_name=&#39;私聊&#39;;
    // 订阅 worker-<id 事件,并注册事件处理函数
    Channel\Client::on($event_name,function($event_data)use($ws_worker){
 
        //print_r($event_data);
        //print_r($ws_worker->connections);
        $to_connect_id=$event_data[&#39;to_connection_id&#39;];
        $message=$event_data[&#39;content&#39;];
 
        foreach ($ws_worker->connections as $connection) {
 
            if($connection->id==$to_connect_id)
            {
                $connection->send($message);
            }
                 
        }
 
        // if(!isset($ws_worker->connections[$to_connect_id]))
        // {
        //     echo &#39;connect is not exist\n&#39;;
        //     return;
        // }
        // $to_connection=$ws_worker->connections[$to_connect_id];
        // $to_connection->send($message);
    });
 
    // 订阅广播事件
    $event_name = &#39;广播&#39;;
    // 收到广播 向所有客户端发送消息
    Channel\Client::on($event_name,function($event_data)use($ws_worker){
        //print_r($event_data);
        $message=$event_data[&#39;content&#39;];
        foreach ($ws_worker->connections as $connection) {
            $connection->send($message);
        }
    });
};
Copier après la connexion

Enregistrez deux événements, un événement de diffusion, un événement de chat privé, une diffusion pour notification en ligne et un message de groupe. Le chat privé est un chat privé. . Ici, vous pouvez également effectuer des envois groupés. Cependant, cette version n'a pas encore été implémentée.

Ensuite, il y a un rappel pour le lien client.

$ws_worker->onConnect=function($connection){
    $connection->id = md5($connection->id."_".time()."_".rand(10000,99999));
};
Copier après la connexion

Ici, le client rappelle, et je vais modifier l'identifiant de connexion du client. Un simple md5 sert principalement à empêcher que les identifiants de série ne soient exploités trop facilement. .

Ensuite, le corps principal de l'ensemble du projet, le rappel du traitement des messages côté serveur.

Pour chaque client entrant, attribuez un identifiant unique

Maintenir une table de relations avec connectid=>user

En raison de l'ouverture de plusieurs processus, il est enregistré dans la session est invalide, il est donc prévu de le stocker dans la base de données

Lorsque le lien est déconnecté, supprimez les données

$ws_worker->onMessage = function($connection, $data)
{
    $res=array(&#39;code&#39;=>200, &#39;msg&#39;=>&#39;ok&#39;, &#39;data&#39;=>null,&#39;type&#39;=>1);
    // 向客户端发送hello $data
    //print_r($data);
    $data=json_decode($data,true);
    //print_r($data);
    if(!isset($data[&#39;type&#39;])||empty($data[&#39;type&#39;]))// type 1  2
    {
        $res=array(&#39;code&#39;=>301, &#39;msg&#39;=>&#39;消息包格式错误&#39;, &#39;data&#39;=>null);
    }else{
        switch ($data[&#39;type&#39;]) {
            case &#39;1&#39;: // 客户端上线消息
                //print_r($connection->id);
                 
                if(!isset($data[&#39;user&#39;])||empty($data[&#39;user&#39;]))
                {
                    $res=array(&#39;code&#39;=>301, &#39;msg&#39;=>&#39;消息包格式错误&#39;, &#39;data&#39;=>null);
                    break;
                }
                // 维护一个数组 保存 用户 connection_id => user
 
                $dsn=&#39;mysql:host=127.0.0.1;dbname=kinmoschat;&#39;;
                $pdo=new PDO($dsn,&#39;root&#39;,&#39;123456&#39;);
                //准备SQL语句
                $sql = "INSERT INTO `user`(`connect_id`,`username`) VALUES (:connect_id,:username)";
 
                //调用prepare方法准备查询
                $stmt = $pdo->prepare($sql);
 
                //传递一个数组为预处理查询中的命名参数绑定值,并执行SQL
                $stmt->execute(array(&#39;:connect_id&#39; => $connection->id,&#39;:username&#39; => $data[&#39;user&#39;]));
                //获取最后一个插入数据的ID值
                //echo $pdo->lastInsertId() . &#39;<br />&#39;;
 
                // 向自己推送一条消息
                $res2[&#39;type&#39;]=3;// 系统信息
                $res2[&#39;data&#39;]=array(&#39;userinfo&#39; =>$data[&#39;user&#39;]);// 系统信息
                $connection->send(json_encode($res2));
 
                $msg="用户 ".$data[&#39;user&#39;]." 上线了~~";
                $res[&#39;data&#39;]=$msg;
                break;
            case &#39;2&#39;: // 客户端群发送消息
                if(!isset($data[&#39;user&#39;])||empty($data[&#39;user&#39;])||!isset($data[&#39;msg&#39;])||empty($data[&#39;msg&#39;]))
                {
                    $res=array(&#39;code&#39;=>301, &#39;msg&#39;=>&#39;消息包格式错误&#39;, &#39;data&#39;=>null);
                    break;
                }
                $msg="用户 ".$data[&#39;user&#39;]."说:".$data[&#39;msg&#39;];
                $res[&#39;data&#39;]=$msg;
                break;
            case &#39;3&#39;: // 客户端私聊
                if(!isset($data[&#39;user&#39;])||empty($data[&#39;user&#39;])||!isset($data[&#39;msg&#39;])||empty($data[&#39;msg&#39;])||!isset($data[&#39;friend_id&#39;])||empty($data[&#39;friend_id&#39;]))
                {
                    $res=array(&#39;code&#39;=>301, &#39;msg&#39;=>&#39;消息包格式错误&#39;, &#39;data&#39;=>null);
                    break;
                }
                $msg="用户 ".$data[&#39;user&#39;]."对您说:".$data[&#39;msg&#39;];
                $res[&#39;data&#39;]=$msg;
                $res[&#39;type&#39;]=1;// 聊天消息
                $res1=json_encode($res);
                // 推送给单个用户
                $event_name = &#39;私聊&#39;;
                Channel\Client::publish($event_name, array(
                    &#39;content&#39;          => $res1,
                    &#39;to_connection_id&#39; =>$data[&#39;friend_id&#39;]
                ));
                // 另外还要给自己推条消息
                $msg="您对 ".$data[&#39;friendname&#39;]."说:".$data[&#39;msg&#39;];
                $res[&#39;data&#39;]=$msg;
                $res[&#39;type&#39;]=1;// 聊天消息
                $res2=json_encode($res);
                Channel\Client::publish($event_name, array(
                    &#39;content&#39;          => $res2,
                    &#39;to_connection_id&#39; =>$connection->id
                ));
                return;
                break;
             
            default:
                # code...
                break;
        }
    }
    $res[&#39;type&#39;]=1;// 聊天消息
    $res=json_encode($res);
    // 广播给所有客户端
    $event_name = &#39;广播&#39;;
    Channel\Client::publish($event_name, array(
        &#39;content&#39;          => $res
    ));
 
    $dsn=&#39;mysql:host=127.0.0.1;dbname=kinmoschat;&#39;;
    $dbh=new PDO($dsn,&#39;root&#39;,&#39;123456&#39;);
    $stmt=$dbh->query(&#39;SELECT connect_id,username FROM user&#39;);
    $row=$stmt->fetchAll();
    $uerHtml="";
    foreach ($row as $key => $value) {
 
        $uerHtml.=&#39;<a class="user" onclick="userclick(\&#39;&#39;.$value[&#39;username&#39;].&#39;\&#39;,\&#39;&#39;.$value[&#39;connect_id&#39;].&#39;\&#39;);" value="&#39;.$value[&#39;connect_id&#39;].&#39;" href="javascript:void(0);">&#39;.$value[&#39;username&#39;].&#39;</a><br/>&#39;;
    }
    //print_r($row);
    $res1[&#39;type&#39;]=2;// 用户消息
    $res1[&#39;data&#39;]=$uerHtml;
    $res1=json_encode($res1);
     
 
    $event_name = &#39;广播&#39;;
    Channel\Client::publish($event_name, array(
        &#39;content&#39;          => $res1
    ));
};
Copier après la connexion

Ici, le connectid=>nom de chaque utilisateur sera stocké dans la base de données. .

Lorsqu'un message en ligne est reçu, il sera diffusé à tous les utilisateurs.

Reçu un message de groupe. . Diffusion à tous les clients.

Reçu un message privé. Ensuite, envoyez-le individuellement à vous-même et à la personne qui l'a envoyé.

Écoutez l'événement de fermeture du client. Lorsque le client est fermé, supprimez les enregistrements pertinents dans la table utilisateur

// 关闭链接 将数据库中的该数据删除
$ws_worker->onClose=function($connection)
{
    //echo 3233;
    $dsn=&#39;mysql:host=127.0.0.1;dbname=kinmoschat;&#39;;
    $pdo=new PDO($dsn,&#39;root&#39;,&#39;123456&#39;);
    $sql="delete from user where connect_id=&#39;".$connection->id."&#39;";
    //print_r($sql);
    $pdo->exec($sql);
};
Copier après la connexion
Pour plus de connaissances sur Workerman, veuillez prêter attention au

tutoriel Workerman<.> colonne.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:cnblogs.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal