>  기사  >  백엔드 개발  >  websocket_php 예제를 기반으로 간단한 채팅방을 구축하는 PHP 실습

websocket_php 예제를 기반으로 간단한 채팅방을 구축하는 PHP 실습

WBOY
WBOY원래의
2016-12-05 13:28:141335검색

이 기사의 예에서는 웹소켓을 기반으로 PHP로 간단한 채팅방을 구축하는 방법을 설명합니다. 참고할 수 있도록 모든 사람과 공유하세요. 세부 내용은 다음과 같습니다.
1. 서문

회사 게임에 간단한 채팅방이 있다는 걸 알고나서 node+websocket으로 만든 줄 알았더니 PHP를 이용해 간단한 채팅방을 만들어볼까 하는 생각이 들었습니다. 그래서 다양한 정보를 수집하고, 문서를 읽고, 사례를 찾아보고, 간단한 채팅방을 직접 작성했습니다.

http 연결은 짧은 연결과 긴 연결로 구분됩니다. 짧은 연결은 일반적으로 ajax를 사용하여 구현할 수 있으며 긴 연결은 웹소켓입니다. 짧은 연결은 비교적 구현이 간단하지만 너무 많은 리소스를 소비합니다. Websocket은 효율적이지만 호환성에 몇 가지 문제가 있습니다. websocket은 html5의 리소스입니다

2. 프런트엔드

웹소켓의 프런트엔드 구현은 매우 간단하고 간단합니다

//连接websocket

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

//成功连接websoc的时候

ws.onopen = function(){}

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

ws.onmessage = function(e){}

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

//向服务端发送数据

ws.send();

3. 백스테이지

웹소켓의 어려움은 주로 그 배경에 있습니다

3.1웹소켓 연결과정
웹소켓 통신 다이어그램 이것은 클라이언트와 서버 간의 간단한 통신 다이어그램입니다. PHP가 주로 하는 일은 암호화 키를 수락하고 이를 반환하여 소켓 생성 및 핸드셰이크 작업을 완료하는 것입니다.

아래 사진은 웹소켓을 처리하는 서버의 세부 흐름도입니다

3.2 코드 연습

서버에서 수행되는 프로세스는 대략 다음과 같습니다.

  1. 연결을 기다리는 소켓 프로세스 정지
  2. 소켓 연결 후 소켓 배열 탐색
  3. 핸드셰이크가 없으면 핸드셰이크 작업이 수행됩니다. 핸드셰이크가 있으면 데이터를 수신하고 구문 분석하여 버퍼에 기록하여 출력합니다

다음은 샘플 코드입니다(클래스를 작성했기 때문에 코드가 기능별로 나누어져 있습니다). 텍스트 하단에는 github 주소와 제가 겪은 몇 가지 함정이 나와 있습니다.
1. 먼저 소켓을 생성합니다

//建立套接字
    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;
    }

2. 소켓을 배열에 넣습니다

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

    }

3. 정지된 프로세스가 소켓 배열을 순회하며 여기서 주요 작업이 완료됩니다

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);
            }
          }
        }
      }
    }

4. 핸드셰이크 프로세스는 웹소켓 콘텐츠를 수신하고 Sec-WebSocket-Key에서 키를 얻은 다음 암호화 알고리즘을 통해 이를 버퍼에 쓰는 것입니다(자동 확인에는 처리가 필요하지 않습니다. )

public function hands($client,$buff,$v)
    {
      //提取websocket传的key并进行加密 (这是固定的握手机制获取Sec-WebSocket-Key:里面的key)
      $buf = substr($buff,strpos($buff,'Sec-WebSocket-Key:')+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;
    }

5. 클라이언트 데이터를 구문 분석합니다. (여기에서는 암호화하지 않았습니다. 필요한 경우 직접 암호화할 수 있습니다.)

//解析数据
    public function decodeData($buff)
    {
      //$buff 解析数据帧
      $mask = array(); 
      $data = ''; 
      $msg = unpack('H*',$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(
            'mess'=>$block[0],
            );
        return $mess;          
      }

6. 버퍼에 소켓 쓰기

//发送数据
    public function send($mess,$v)
    {
      //遍历套接字数组 成功握手的 进行数据群发
      foreach ($this->socs as $keys => $values) {
        //用系统分配的套接字资源id作为用户昵称
          $mess['name']="Tourist'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));
        }
    }

7. 운영방법

github 주소 git@github.com:rsaLive/websocket.git

①server.php를 콘솔에서 실행하는 것이 가장 좋습니다

server.php 스크립트 디렉터리로 이동합니다(먼저 php -v를 실행하여 php가 구성되어 있는지 확인할 수 있습니다. Linux 구성이 없으면 bash 창에서 경로를 구성하세요)

php -f 서버.php

오류가 있으면 메시지가 표시됩니다

②서버를 통해 html 파일에 접근

8. 밟힌 함정에 대해서는 디버깅을 열어 오류를 확인하세요

①server.php는 중단된 프로세스에서 출력을 인쇄할 수 있습니다. 문제가 발생하면 코드에 인쇄를 추가하여 디버깅할 수 있습니다

각 판단을 표시하고 코드가 어느 섹션에서 실행되고 있는지 콘솔에서 확인할 수 있습니다

단, 코드를 수정한 후에는 매번 php server.php 스크립트를 다시 실행해야 합니다

②이 오류가 발생하면

1. 서버와 소켓 초기화 시 데이터를 보냅니다. (서버와의 첫 번째 확인 핸드셰이크 중에는 콘텐츠를 보낼 수 없습니다.)

2. 확인되었으나 클라이언트가 전송하지 않았거나 보낸 메시지가 비어 있는 경우에도 마찬가지입니다

그럼 연결된 소켓의 데이터를 확인해보세요

③ 브라우저가 지원하지 않거나 서버가 소켓을 열지 않을 수도 있습니다. 시작하기 전에 확인하는 것이 가장 좋습니다

if (window.WebSocket){
  console.log("This browser supports WebSocket!");
} else {
  console.log("This browser does not support WebSocket.");
}

위 글의 내용은 모두의 공부에 도움이 되길 바랍니다.

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.