PHP使用socket實現聊天通訊


代碼進行簡單配置,能直接跑起來。直接上代碼

class ws_socket{

  public $socket = null;     //服務端創建的socket

  public $sockts = [];        //連接池

  public $user= [];            //保存所有鏈接的用戶

  public $write = null;       //socket_select第二個參數

  public $except = null;    //socket_select第三個參數  

  public function __construct($ip,$port){

    $this->socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);    //創建

    socket_set_option($this->socket,SOL_SOCKET,SO_REUSEADDR,true);    //設置

    socket_bind($this->socket,$ip,$port);                   //綁定

    socket_listen($this->socket);                         //監聽

  }

  //用戶連接開始聊天
  public function run(){
    $this->sockts[] = $this->socket;
    while(true){
      $tmp_sockets = $this->sockts;
      socket_select($tmp_sockets,$write,$except,null);   //關鍵函數,IO復用,多用戶鏈接(1,2,3參數都是引用傳值)

      foreach($tmp_sockets as $sock){
        if($sock==$this->socket){
          $conSock = socket_accept($this->socket);    //新用戶,保存到連接池
          $this->sockts[] = $conSock;
          $this->user[] = ['socket'=>$conSock,'handshake'=>false];
        }else{
          
          $request = socket_read($sock,8024);       //接收用戶消息
          $k = $this->getUserIndex($sock);       //找到當前發送用的在連接池的$k
          if(strlen($request)==8){            //用戶退出 關閉該連接池
            $this->close($k);           //關閉當前關閉的鏈接
            continue;
          }
          //判斷是否有握手,沒有握手先握手,有握手直接發生消息
          if(!$this->user[$k]['handshake']){
            $response = $this->handleShake($request);
            socket_write($sock,$response,strlen($response));
            $this->user[$k]['handshake'] = true;
          }else{
            $msg = $this->decode($request);
            $jsonArr = json_decode($msg);
            $this->send($jsonArr,$k);
          }
        }
      }
    }
  }

  //關閉鏈接
  private function close($k){
    //找到關閉的鏈接 並在連接池刪除
    socket_close($this->user[$k]['socket']);
    unset($this->user[$k]);
    //將連接池清空,再將user重新賦值給連接池
    $this->sockts = null;
    $this->sockts[] = $this->socket;
    foreach($this->user as $v){
      $this->sockts[] = $v['socket'];
    }
  }

  //獲取用戶的$k
  private function getUserIndex($sock){
    foreach($this->user as $k=>$v){
      if($v['socket']==$sock){
        return $k;
      }
    }
  }

  //編碼函數
  private function encode($msg) {
    $frame = [];
    $frame[0] = '81';
    $len = strlen($msg);
    if ($len < 126) {
      $frame[1] = $len < 16 ? '0' . dechex($len) : dechex($len);
    } else if ($len < 65025) {
      $s = dechex($len);
      $frame[1] = '7e' . str_repeat('0', 4 - strlen($s)) . $s;
    } else {
      $s = dechex($len);
      $frame[1] = '7f' . str_repeat('0', 16 - strlen($s)) . $s;
    }
    $data = '';
    $l = strlen($msg);
    for ($i = 0; $i < $l; $i++) {
      $data .= dechex(ord($msg{$i}));
    }
    $frame[2] = $data;
    $data = implode('', $frame);
    return pack("H*", $data);
  }

  //編碼函數
  private function decode($buffer) {
    $decoded = '';
    if ( !empty($buffer[1]) ) {
      $len = ord($buffer[1]) & 127;
    }else{
      $len = '';
    }
    if ($len === 126) {
      $masks = substr($buffer, 4, 4);
      $data = substr($buffer, 8);
    } else if ($len === 127) {
      $masks = substr($buffer, 10, 4);
      $data = substr($buffer, 14);
    } else {
      $masks = substr($buffer, 2, 4);
      $data = substr($buffer, 6);
    }
    for ($index = 0; $index < strlen($data); $index++) {
      $decoded .= $data[$index] ^ $masks[$index % 4];
    }
    return $decoded;
  }

  //握手函數
  private function handleShake($request){
    $buf = substr($request,strpos($request,'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";
    return $new_message;
  }

  //發送消息,業務處理  $msg參數是客服端發送過來的信息
  private function send($msg,$k){

    //需要發送的數據

    $res['replyname'] = '小白';
    $res['replyid'] = 1;
    $res['msgtype'] = 2;
    $res['isline'] = '在線';
    $res = $this->encode(json_encode($res));

    socket_write($this->user[$k]['socket'],$res,strlen($res));    //發送

  }

}

$obj = new Ws(0,8888);
$obj->run();

后面會把webSocket的代碼也分享出來,有什么問題評論留言


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM