Pratique PHP consistant à créer une salle de discussion basée sur Websocket

墨辰丷
Libérer: 2023-03-28 16:12:01
original
7193 Les gens l'ont consulté

Cet article explique principalement la pratique de création d'un salon de discussion simple avec php+websocket. Pour le contenu sur php et websocket, veuillez vous y référer pour ceux qui en ont besoin.

L'exemple de cet article décrit la pratique de création d'une salle de discussion simple en PHP basée sur websocket. Partagez-le avec tout le monde pour votre référence. Les détails sont les suivants :
1. Avant-propos

Il y a un simple salon de discussion dans le jeu de l'entreprise. Après en avoir pris connaissance, j'ai réalisé qu'il avait été créé par node+websocket. . J'ai aussi pensé à en créer un avec PHP. J’ai donc collecté diverses informations, lu des documents, cherché des exemples et écrit moi-même un simple salon de discussion.

Les connexions http sont divisées en connexions courtes et connexions longues. Les connexions courtes peuvent généralement être implémentées à l'aide d'Ajax, et les connexions longues sont des websockets. Les connexions courtes sont relativement simples à mettre en œuvre, mais consomment trop de ressources. Websocket est efficace mais présente quelques problèmes de compatibilité. Websocket est une ressource HTML5

2. Front-end

L'implémentation frontale de Websocket est très simple et directe

//连接websocket

var ws = new WebSocket("ws://127.0.0.1:8000");

//成功连接websoc的时候

ws.onopen = function(){}

//成功获取服务端输出的消息

ws.onmessage = function(e){}

//连接错误的时候
ws.onerror = function(){}

//向服务端发送数据

ws.send();
Copier après la connexion

3. Backend

La difficulté du websocket réside principalement dans le backend

3.1 websocket processus de connexion
Schéma de communication Websocket Il s'agit d'un simple diagramme de communication entre le client et le serveur. Ce que PHP fait principalement est d'accepter la clé de cryptage et de la renvoyer pour terminer la création du socket et l'opération de prise de contact

<🎜. >

Comme indiqué ci-dessous, il s'agit d'un organigramme détaillé du websocket de traitement du serveur


3.2 Pratique du code


Le processus effectué par le serveur est à peu près le suivant :

  1. Suspendre un processus de socket en attente de connexion

  2. Parcourir le tableau de sockets après une connexion socket

  3. S'il n'y a pas de prise de contact, effectuez l'opération de prise de contact. S'il y a déjà une prise de contact, les données reçues seront analysées et écrites dans le tampon pour la sortie. 🎜>


  4. Ce qui suit est l'exemple de code (ce que j'ai écrit est une classe A (donc le code est segmenté selon les fonctions), le bas du texte donne l'adresse github et quelques pièges J'ai rencontré
1. Tout d'abord, créez un socket



//建立套接字
    public function createSocket($address,$port)
    {
      //创建一个套接字
      $socket= socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
      //设置套接字选项
      socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);
      //绑定IP地址和端口
      socket_bind($socket,$address,$port);
      //监听套接字
      socket_listen($socket);
      return $socket;
    }
Copier après la connexion

2. Mettez le socket dans le tableau

.

public function __construct($address,$port)
    {
      //建立套接字
      $this->soc=$this->createSocket($address,$port);
      $this->socs=array($this->soc);

    }
Copier après la connexion

3. Suspendez le processus pour parcourir le socket Le tableau d'interface, les opérations principales sont terminées ici


public function run(){
      //挂起进程
      while(true){
        $arr=$this->socs;
        $write=$except=NULL;
        //接收套接字数字 监听他们的状态
        socket_select($arr,$write,$except, NULL);
        //遍历套接字数组
        foreach($arr as $k=>$v){
          //如果是新建立的套接字返回一个有效的 套接字资源
          if($this->soc == $v){
            $client=socket_accept($this->soc);
            if($client <0){
              echo "socket_accept() failed";
            }else{
              // array_push($this->socs,$client);
              // unset($this[]);
              //将有效的套接字资源放到套接字数组
              $this->socs[]=$client;
            }
          }else{
            //从已连接的socket接收数据 返回的是从socket中接收的字节数
            $byte=socket_recv($v, $buff,20480, 0);
            //如果接收的字节是0
            if($byte<7)
              continue;
            //判断有没有握手没有握手则进行握手,如果握手了 则进行处理
            if(!$this->hand[(int)$client]){
              //进行握手操作
              $this->hands($client,$buff,$v);
            }else{
              //处理数据操作
              $mess=$this->decodeData($buff);
                //发送数据
              $this->send($mess,$v);
            }
          }
        }
      }
    }
Copier après la connexion

4. Le processus de prise de contact consiste à recevoir le contenu du websocket de Sec-WebSocket -Key : obtenez la clé et écrivez-la dans le tampon via l'algorithme de cryptage. Le client la vérifiera (automatiquement. la vérification ne nécessite pas notre traitement)


public function hands($client,$buff,$v)
    {
      //提取websocket传的key并进行加密 (这是固定的握手机制获取Sec-WebSocket-Key:里面的key)
      $buf = substr($buff,strpos($buff,&#39;Sec-WebSocket-Key:&#39;)+18);
      //去除换行空格字符
      $key = trim(substr($buf,0,strpos($buf,"\r\n")));
       //固定的加密算法
      $new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
      $new_message = "HTTP/1.1 101 Switching Protocols\r\n";
      $new_message .= "Upgrade: websocket\r\n";
      $new_message .= "Sec-WebSocket-Version: 13\r\n";
      $new_message .= "Connection: Upgrade\r\n";
      $new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
      //将套接字写入缓冲区
      socket_write($v,$new_message,strlen($new_message));
      // socket_write(socket,$upgrade.chr(0), strlen($upgrade.chr(0)));
      //标记此套接字握手成功
      $this->hand[(int)$client]=true;
    }
Copier après la connexion

5. Analyser les données du client (je ne les ai pas cryptées ici, vous pouvez les crypter vous-même). si nécessaire)


//解析数据
    public function decodeData($buff)
    {
      //$buff 解析数据帧
      $mask = array(); 
      $data = &#39;&#39;; 
      $msg = unpack(&#39;H*&#39;,$buff); //用unpack函数从二进制将数据解码
      $head = substr($msg[1],0,2); 
      if (hexdec($head{1}) === 8) { 
        $data = false; 
      }else if (hexdec($head{1}) === 1){ 
        $mask[] = hexdec(substr($msg[1],4,2)); 
        $mask[] = hexdec(substr($msg[1],6,2)); 
        $mask[] = hexdec(substr($msg[1],8,2)); 
        $mask[] = hexdec(substr($msg[1],10,2)); 
          //遇到的问题 刚连接的时候就发送数据 显示 state connecting
        $s = 12; 
        $e = strlen($msg[1])-2; 
        $n = 0; 
        for ($i=$s; $i<= $e; $i+= 2) { 
          $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2))); 
          $n++; 
        }
        //发送数据到客户端
          //如果长度大于125 将数据分块
          $block=str_split($data,125);
          $mess=array(
            &#39;mess&#39;=>$block[0],
            );
        return $mess;          
      }
Copier après la connexion

6. Utiliser l'astuce Tampon d'écriture de l'interface


//发送数据
    public function send($mess,$v)
    {
      //遍历套接字数组 成功握手的 进行数据群发
      foreach ($this->socs as $keys => $values) {
        //用系统分配的套接字资源id作为用户昵称
          $mess[&#39;name&#39;]="Tourist&#39;s socket:{$v}";
          $str=json_encode($mess);
          $writes ="\x81".chr(strlen($str)).$str;
          // ob_flush();
          // flush();
          // sleep(3);
          if($this->hand[(int)$values])
            socket_write($values,$writes,strlen($writes));
        }
    }
Copier après la connexion

7. Exécution de la méthode

adresse github git@github.com:rsaLive/ websocket.git

①Il est préférable d'exécuter server.php dans la console

Allez dans le répertoire du script server.php (vous pouvez d'abord php -v pour voir si php est configuré. S'il n'y a pas de configuration Linux, bash Path sous la configuration Windows)

php -f server .php

S'il y a une erreur, il vous demandera

②Accéder aux fichiers HTML via le serveur

8. Si vous avez marché sur les pièges, ouvrez le débogage pour faciliter la visualisation des erreurs

①server.php peut imprimer la sortie. dans le processus suspendu. Si un problème survient, vous pouvez ajouter une impression au code pour déboguer

Vous pouvez marquer chaque jugement et vérifier dans la console dans quelle section le code est exécuté

Cependant, vous il faut réexécuter le script php server.php à chaque fois après avoir modifié le code

②Si cette erreur se produit, cela peut être

1. lors de l'initialisation du socket avec le serveur (le contenu ne peut pas être envoyé lors de la première poignée de main de vérification avec le serveur)

2 S'il a été vérifié mais que le client ne l'a pas envoyé ou que le message envoyé est vide. arrive

vous devez donc vérifier les données de la socket connectée

③ Peut-être que le navigateur ne le prend pas en charge ou que le serveur n'ouvre pas la socket pour commencer. Il est préférable de vérifier au préalable

if (window.WebSocket){
  console.log("This browser supports WebSocket!");
} else {
  console.log("This browser does not support WebSocket.");
}
Copier après la connexion

Ce qui précède est l'intégralité du contenu de cet article, j'espère qu'il sera utile à l'étude de chacun.


Recommandations associées :

Explication détaillée de l'algorithme d'enveloppe rouge aléatoire PHP


Faites-le avec PHP Rechercher des personnes à proximité

Analyse des cas d'utilisation des connexions longues PHP

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:php.cn
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
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!