Heim >Backend-Entwicklung >PHP-Tutorial >php+webSoket implementiert Chatroom-Beispielcode

php+webSoket implementiert Chatroom-Beispielcode

高洛峰
高洛峰Original
2017-02-20 17:42:441278Durchsuche

本篇文章主要介绍了php+webSoket搭建chatServer示例(附源码)的资料,这里整理了详细的代码,有需要的小伙伴可以参考下。

最近在公司利用直播间搭建一个图文直播间时正好要用到chatsever,研究了一下html5的websocket 实现了双向通信,根据前人的经验折腾了几天弄了个聊天室,实现了发送图片,发送QQ表情,群聊私聊等功能,特地分享给各位新手参考学习,大牛可以忽略。

前端:client.html


<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/>
<title>HTML5 websocket 网页聊天室 javascript php</title>
<style type="text/css">
body,p{margin:0px; padding:0px; font-size:14px; color:#333; font-family:Arial, Helvetica, sans-serif;}
#ltian,.rin{width:98%; margin:5px auto;}
#ltian{border:1px #ccc solid;overflow-y:auto; overflow-x:hidden; position:relative;}
#ct{margin-right:111px; height:100%;overflow-y:auto;overflow-x: hidden;}
#us{width:110px; overflow-y:auto; overflow-x:hidden; float:right; border-left:1px #ccc solid; height:100%; background-color:#F1F1F1;}
#us p{padding:3px 5px; color:#08C; line-height:20px; height:20px; cursor:pointer; overflow:hidden; white-space:nowrap; text-overflow:ellipsis;}
#us p:hover,#us p:active,#us p.ck{background-color:#069; color:#FFF;}
#us p.my:hover,#us p.my:active,#us p.my{color:#333;background-color:transparent;}
button{float:right; width:80px; height:35px; font-size:18px;}
input{width:100%; height:30px; padding:2px; line-height:20px; outline:none; border:solid 1px #CCC;}
.rin p{margin-right:160px;}
.rin span{float:right; padding:6px 5px 0px 5px; position:relative;}
.rin span img{margin:0px 3px; cursor:pointer;}
.rin span form{position:absolute; width:25px; height:25px; overflow:hidden; opacity:0; top:5px; right:5px;}
.rin span input{width:180px; height:25px; margin-left:-160px; cursor:pointer}

#ct p{padding:5px; line-height:20px;}
#ct a{color:#069; cursor:pointer;}
#ct span{color:#999; margin-right:10px;}
.c2{color:#999;}
.c3{background-color:#DBE9EC; padding:5px;}
.qp{position:absolute; font-size:12px; color:#666; top:5px; right:130px; text-decoration:none; color:#069;}
#ems{position:absolute; z-index:5; display:none; top:0px; left:0px; max-width:230px; background-color:#F1F1F1; border:solid 1px #CCC; padding:5px;}
#ems img{width:44px; height:44px; border:solid 1px #FFF; cursor:pointer;}
#ems img:hover,#ems img:active{border-color:#A4B7E3;}
#ems a{color:#069; border-radius:2px; display:inline-block; margin:2px 5px; padding:1px 8px; text-decoration:none; background-color:#D5DFFD;}
#ems a:hover,#ems a:active,#ems a.ck{color:#FFF; background-color:#069;}
.tc{text-align:center; margin-top:5px;}
</style>
</head>


<body>
<p id="ltian">
 <p id="us" class="jb"></p>
 <p id="ct"></p>
 <a href="javascript:;" rel="external nofollow" rel="external nofollow" class="qp" onClick="this.parentNode.children[1].innerHTML=&#39;&#39;">清屏</a>
</p>
<p class="rin">
 <button id="sd">发送</button>
 <span><img  src="http://www.yxsss.com/ui/sk/t.png" title="表情" id="imgbq" alt="php+webSoket implementiert Chatroom-Beispielcode" ><img  src="http://www.yxsss.com/ui/sk/e.png" title="上传图片" alt="php+webSoket implementiert Chatroom-Beispielcode" ><form><input type="file" title="上传图片" id="upimg"></form></span>
 <p><input id="nrong"></p>
</p>
<p id="ems"><p></p><p class="tc"></p></p>
<script>
if(typeof(WebSocket)==&#39;undefined&#39;){
 alert(&#39;你的浏览器不支持 WebSocket ,推荐使用Google Chrome 或者 Mozilla Firefox&#39;); 
}
</script>
<script src="http://www.yxsss.com/ui/p/a.js" type="text/javascript"></script>
<script>
(function(){
 var key=&#39;all&#39;,mkey;
 var users={};
 var url=&#39;ws://127.0.0.1:8000&#39;;
 var so=false,n=false;
 var lus=A.$(&#39;us&#39;),lct=A.$(&#39;ct&#39;);
 function st(){
  var Arr1 = ["聪明的","狡猾的","可爱的","美丽的","狡猾的","善良的","帅气的","逗比的"]; 
  var Arr2 = ["大灰狼","小白兔","母老虎","外星人","皮卡丘","HelloKitty","吴亦凡","薛之谦"]; 
  var ran1 = Math.floor(Math.random() * Arr1.length + 1)-1; 
  var ran2 = Math.floor(Math.random() * Arr2.length + 1)-1; 
  var n=Arr1[ran1]+Arr2[ran2]; 
  //以上五行是用来随机生成用户昵称的方法,参考一下 ,如果想自定义用户名可以将以上五行注释,然后以下两行取消注释
  //n=prompt(&#39;请给自己取一个霸气的名字:&#39;);
  //n=n.substr(0,16);
  //console.log(n);
  if(!n){
   return ; 
  }
  so=new WebSocket(url);
  so.onopen=function(){
   if(so.readyState==1){
    so.send(&#39;type=add&ming=&#39;+n);
   }
  }
  
  so.onclose=function(){
   so=false;
   lct.appendChild(A.$$(&#39;<p class="c2">退出聊天室</p>&#39;));
  }
  
  so.onmessage=function(msg){
   eval(&#39;var da=&#39;+msg.data);
   var obj=false,c=false;
   if(da.type==&#39;add&#39;){
    var obj=A.$$(&#39;<p>&#39;+da.name+&#39;</p>&#39;);
    lus.appendChild(obj);
    cuser(obj,da.code);
    obj=A.$$(&#39;<p><span>[&#39;+da.time+&#39;]</span>欢迎<a>&#39;+da.name+&#39;</a>加入</p>&#39;);
    c=da.code;
   }else if(da.type==&#39;madd&#39;){
    mkey=da.code;
    da.users.unshift({&#39;code&#39;:&#39;all&#39;,&#39;name&#39;:&#39;大家&#39;});
    for(var i=0;i<da.users.length;i++){
     var obj=A.$$(&#39;<p>&#39;+da.users[i].name+&#39;</p>&#39;);
     lus.appendChild(obj);
     if(mkey!=da.users[i].code){
      cuser(obj,da.users[i].code);
     }else{
      obj.className=&#39;my&#39;;
      document.title=da.users[i].name;
     }
    }
    obj=A.$$(&#39;<p><span>[&#39;+da.time+&#39;]</span>欢迎&#39;+da.name+&#39;加入</p>&#39;);
    users.all.className=&#39;ck&#39;;
   }
   
   if(obj==false){
    if(da.type==&#39;rmove&#39;){
     var obj=A.$$(&#39;<p class="c2"><span>[&#39;+da.time+&#39;]</span>&#39;+users[da.nrong].innerHTML+&#39;退出聊天室</p>&#39;);
     lct.appendChild(obj);
     users[da.nrong].del();
     delete users[da.nrong];
    }else{
     da.nrong=da.nrong.replace(/{\\(\d+)}/g,function(a,b){
      return &#39;<img  src="sk/&#39;+b+&#39;.jpg" alt="php+webSoket implementiert Chatroom-Beispielcode" >&#39;;
     }).replace(/^data\:image\/png;base64\,.{50,}$/i,function(a){
      return &#39;<img  src="&#39;+a+&#39;" alt="php+webSoket implementiert Chatroom-Beispielcode" >&#39;;
     });
     //da.code 发信息人的code
     if(da.code1==mkey){
      obj=A.$$(&#39;<p class="c3"><span>[&#39;+da.time+&#39;]</span><a>&#39;+users[da.code].innerHTML+&#39;</a>对我说:&#39;+da.nrong+&#39;</p>&#39;);
      c=da.code;
     }else if(da.code==mkey){
      if(da.code1!=&#39;all&#39;)
      obj=A.$$(&#39;<p class="c3"><span>[&#39;+da.time+&#39;]</span>我对<a>&#39;+users[da.code1].innerHTML+&#39;</a>说:&#39;+da.nrong+&#39;</p>&#39;);
      else
      obj=A.$$(&#39;<p><span>[&#39;+da.time+&#39;]</span>我对<a>&#39;+users[da.code1].innerHTML+&#39;</a>说:&#39;+da.nrong+&#39;</p>&#39;);
      c=da.code1;
     }else if(da.code==false){
      obj=A.$$(&#39;<p><span>[&#39;+da.time+&#39;]</span>&#39;+da.nrong+&#39;</p>&#39;);
     }else if(da.code1){
      obj=A.$$(&#39;<p><span>[&#39;+da.time+&#39;]</span><a>&#39;+users[da.code].innerHTML+&#39;</a>对&#39;+users[da.code1].innerHTML+&#39;说:&#39;+da.nrong+&#39;</p>&#39;);
      c=da.code;
     }
    }
   }
   if(c){
     obj.children[1].onclick=function(){
      users[c].onclick();
     }
    }
   lct.appendChild(obj);
   lct.scrollTop=Math.max(0,lct.scrollHeight-lct.offsetHeight);
  }
 }
 A.$(&#39;sd&#39;).onclick=function(){
  if(!so){
    return st();
  }
  var da=A.$(&#39;nrong&#39;).value.trim();
  if(da==&#39;&#39;){
   alert(&#39;内容不能为空&#39;);
   return false; 
  }
  A.$(&#39;nrong&#39;).value=&#39;&#39;;
  so.send(&#39;nr=&#39;+esc(da)+&#39;&key=&#39;+key);
 }
 A.$(&#39;nrong&#39;).onkeydown=function(e){
  var e=e||event;
  if(e.keyCode==13){
   A.$(&#39;sd&#39;).onclick();
  }
 }
 function esc(da){
  da=da.replace(/</g,&#39;<&#39;).replace(/>/g,&#39;>&#39;).replace(/\"/g,&#39;"&#39;);
  return encodeURIComponent(da);
 }
 function cuser(t,code){
  users[code]=t;
  t.onclick=function(){
   t.parentNode.children.rcss(&#39;ck&#39;,&#39;&#39;);
   t.rcss(&#39;&#39;,&#39;ck&#39;);
   key=code;
  }
 }
 A.$(&#39;ltian&#39;).style.height=(document.documentElement.clientHeight - 70)+&#39;px&#39;;
 st();
 

 var bq=A.$(&#39;imgbq&#39;),ems=A.$(&#39;ems&#39;);
 var l=80,r=4,c=5,s=0,p=Math.ceil(l/(r*c));
 var pt=&#39;sk/&#39;;
 bq.onclick=function(e){
  var e=e||event;
  if(!so){
    return st();
  }
  ems.style.display=&#39;block&#39;;
  document.onclick=function(){
   gb(); 
  }
  ct();
  try{e.stopPropagation();}catch(o){}
 }
 
 for(var i=0;i<p;i++){
  var a=A.$$(&#39;<a href="javascript:;" rel="external nofollow" rel="external nofollow" >&#39;+(i+1)+&#39;</a>&#39;);
  ems.children[1].appendChild(a);
  ef(a,i);
 }
 ems.children[1].children[0].className=&#39;ck&#39;;
 
 function ct(){
  var wz=bq.weiz();
  with(ems.style){
   top=wz.y-242+&#39;px&#39;;
   left=wz.x+bq.offsetWidth-235+&#39;px&#39;;
  }
 }
  
 function ef(t,i){
  t.onclick=function(e){
   var e=e||event;
   s=i*r*c;
   ems.children[0].innerHTML=&#39;&#39;;
   hh();
   this.parentNode.children.rcss(&#39;ck&#39;,&#39;&#39;);
   this.rcss(&#39;&#39;,&#39;ck&#39;);
   try{e.stopPropagation();}catch(o){}
  }
 }
 
 function hh(){
  var z=Math.min(l,s+r*c);
  for(var i=s;i<z;i++){
   var a=A.$$(&#39;<img  src="&#39;+pt+i+&#39;.jpg" alt="php+webSoket implementiert Chatroom-Beispielcode" >&#39;);
   hh1(a,i);
   ems.children[0].appendChild(a);
  }
  ct();
 }
 
 function hh1(t,i){
  t.onclick=function(e){
   var e=e||event;
   A.$(&#39;nrong&#39;).value+=&#39;{\\&#39;+i+&#39;}&#39;;
   if(!e.ctrlKey){
    gb();
   }
   try{e.stopPropagation();}catch(o){}
  }
 }
 
 function gb(){
  ems.style.display=&#39;&#39;;
  A.$(&#39;nrong&#39;).focus();
  document.onclick=&#39;&#39;;
 }
 hh();
 A.on(window,&#39;resize&#39;,function(){
  A.$(&#39;ltian&#39;).style.height=(document.documentElement.clientHeight - 70)+&#39;px&#39;;
  ct();
 }) 

 var fimg=A.$(&#39;upimg&#39;);
 var img=new Image();
 var dw=400,dh=300;
 A.on(fimg,&#39;change&#39;,function(ev){
  if(!so){
   st();
   return false;
  }
  if(key==&#39;all&#39;){
   alert(&#39;由于资源限制 发图只能私聊&#39;);
   return false; 
  }
  var f=ev.target.files[0];
  if(f.type.match(&#39;image.*&#39;)){
   var r = new FileReader();
   r.onload = function(e){
    img.setAttribute(&#39;src&#39;,e.target.result);
   };
   r.readAsDataURL(f);
  }
 });
 img.onload=function(){
  ih=img.height,iw=img.width;
  if(iw/ih > dw/dh && iw > dw){
   ih=ih/iw*dw;
   iw=dw;
  }else if(ih > dh){
   iw=iw/ih*dh;
   ih=dh;
  }
  var rc = A.$$(&#39;canvas&#39;);
  var ct = rc.getContext(&#39;2d&#39;);
  rc.width=iw;
  rc.height=ih;
  ct.drawImage(img,0,0,iw,ih);
  var da=rc.toDataURL();
  so.send(&#39;nr=&#39;+esc(da)+&#39;&key=&#39;+key);
 }
 
})();
</script>
</body>
</html>


后端代码:webserver.php


<?php
error_reporting(E_ALL ^ E_NOTICE);
ob_implicit_flush();

$sk=new Sock(&#39;127.0.0.1&#39;,8000);
$sk->run();
class Sock{
 public $sockets;
 public $users;
 public $master;
 
 private $sda=array();//已接收的数据
 private $slen=array();//数据总长度
 private $sjen=array();//接收数据的长度
 private $ar=array();//加密key
 private $n=array();
 
 public function __construct($address, $port){
 $this->master=$this->WebSocket($address, $port);
 $this->sockets=array($this->master);
 }
 
 
 function run(){
 while(true){
 $changes=$this->sockets;
 $write=NULL;
 $except=NULL;
 socket_select($changes,$write,$except,NULL);
 foreach($changes as $sock){
 if($sock==$this->master){
  $client=socket_accept($this->master);
  $key=uniqid();
  $this->sockets[]=$client;
  $this->users[$key]=array(
  &#39;socket&#39;=>$client,
  &#39;shou&#39;=>false
  );
 }else{
  $len=0;
  $buffer=&#39;&#39;;
  do{
  $l=socket_recv($sock,$buf,1000,0);
  $len+=$l;
  $buffer.=$buf;
  }while($l==1000);
  $k=$this->search($sock);
  if($len<7){
  $this->send2($k);
  continue;
  }
  if(!$this->users[$k][&#39;shou&#39;]){
  $this->woshou($k,$buffer);
  }else{
  $buffer = $this->uncode($buffer,$k);
  if($buffer==false){
  continue;
  }
  $this->send($k,$buffer);
  }
 }
 }
 
 }
 
 }
 
 function close($k){
 socket_close($this->users[$k][&#39;socket&#39;]);
 unset($this->users[$k]);
 $this->sockets=array($this->master);
 foreach($this->users as $v){
 $this->sockets[]=$v[&#39;socket&#39;];
 }
 $this->e("key:$k close");
 }
 
 function search($sock){
 foreach ($this->users as $k=>$v){
 if($sock==$v[&#39;socket&#39;])
 return $k;
 }
 return false;
 }
 
 function WebSocket($address,$port){
 $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
 socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
 socket_bind($server, $address, $port);
 socket_listen($server);
 $this->e(&#39;Server Started : &#39;.date(&#39;Y-m-d H:i:s&#39;));
 $this->e(&#39;Listening on : &#39;.$address.&#39; port &#39;.$port);
 return $server;
 }
 
 
 function woshou($k,$buffer){
 $buf = substr($buffer,strpos($buffer,&#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($this->users[$k][&#39;socket&#39;],$new_message,strlen($new_message));
 $this->users[$k][&#39;shou&#39;]=true;
 return true;
 
 }
 
 function uncode($str,$key){
 $mask = array(); 
 $data = &#39;&#39;; 
 $msg = unpack(&#39;H*&#39;,$str);
 $head = substr($msg[1],0,2); 
 if ($head == &#39;81&#39; && !isset($this->slen[$key])) { 
 $len=substr($msg[1],2,2);
 $len=hexdec($len);
 if(substr($msg[1],2,2)==&#39;fe&#39;){
 $len=substr($msg[1],4,4);
 $len=hexdec($len);
 $msg[1]=substr($msg[1],4);
 }else if(substr($msg[1],2,2)==&#39;ff&#39;){
 $len=substr($msg[1],4,16);
 $len=hexdec($len);
 $msg[1]=substr($msg[1],16);
 }
 $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));
 $s = 12;
 $n=0;
 }else if($this->slen[$key] > 0){
 $len=$this->slen[$key];
 $mask=$this->ar[$key];
 $n=$this->n[$key];
 $s = 0;
 }
 
 $e = strlen($msg[1])-2;
 for ($i=$s; $i<= $e; $i+= 2) { 
 $data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2))); 
 $n++; 
 } 
 $dlen=strlen($data);
 
 if($len > 255 && $len > $dlen+intval($this->sjen[$key])){
 $this->ar[$key]=$mask;
 $this->slen[$key]=$len;
 $this->sjen[$key]=$dlen+intval($this->sjen[$key]);
 $this->sda[$key]=$this->sda[$key].$data;
 $this->n[$key]=$n;
 return false;
 }else{
 unset($this->ar[$key],$this->slen[$key],$this->sjen[$key],$this->n[$key]);
 $data=$this->sda[$key].$data;
 unset($this->sda[$key]);
 return $data;
 }
 
 }
 
 
 function code($msg){
 $frame = array(); 
 $frame[0] = &#39;81&#39;; 
 $len = strlen($msg);
 if($len < 126){
 $frame[1] = $len<16?&#39;0&#39;.dechex($len):dechex($len);
 }else if($len < 65025){
 $s=dechex($len);
 $frame[1]=&#39;7e&#39;.str_repeat(&#39;0&#39;,4-strlen($s)).$s;
 }else{
 $s=dechex($len);
 $frame[1]=&#39;7f&#39;.str_repeat(&#39;0&#39;,16-strlen($s)).$s;
 }
 $frame[2] = $this->ord_hex($msg); 
 $data = implode(&#39;&#39;,$frame); 
 return pack("H*", $data); 
 }
 
 function ord_hex($data) { 
 $msg = &#39;&#39;; 
 $l = strlen($data); 
 for ($i= 0; $i<$l; $i++) { 
 $msg .= dechex(ord($data{$i})); 
 } 
 return $msg; 
 }
 
 //用户加入
 function send($k,$msg){
 parse_str($msg,$g);
 $ar=array();
 if($g[&#39;type&#39;]==&#39;add&#39;){
 $this->users[$k][&#39;name&#39;]=$g[&#39;ming&#39;];
 $ar[&#39;type&#39;]=&#39;add&#39;;
 $ar[&#39;name&#39;]=$g[&#39;ming&#39;];
 $key=&#39;all&#39;;
 }else{
 $ar[&#39;nrong&#39;]=$g[&#39;nr&#39;];
 $key=$g[&#39;key&#39;];
 }
 $this->send1($k,$ar,$key);
 }
 
 function getusers(){
 $ar=array();
 foreach($this->users as $k=>$v){
 $ar[]=array(&#39;code&#39;=>$k,&#39;name&#39;=>$v[&#39;name&#39;]);
 }
 return $ar;
 }
 
 //$k 发信息人的code $key接受人的 code
 function send1($k,$ar,$key=&#39;all&#39;){
 $ar[&#39;code1&#39;]=$key;
 $ar[&#39;code&#39;]=$k;
 $ar[&#39;time&#39;]=date(&#39;m-d H:i:s&#39;);
 $str = $this->code(json_encode($ar));
 if($key==&#39;all&#39;){
 $users=$this->users;
 if($ar[&#39;type&#39;]==&#39;add&#39;){
 $ar[&#39;type&#39;]=&#39;madd&#39;;
 $ar[&#39;users&#39;]=$this->getusers();
 $str1 = $this->code(json_encode($ar));
 socket_write($users[$k][&#39;socket&#39;],$str1,strlen($str1));
 unset($users[$k]);
 }
 foreach($users as $v){
 socket_write($v[&#39;socket&#39;],$str,strlen($str));
 }
 }else{
 socket_write($this->users[$k][&#39;socket&#39;],$str,strlen($str));
 socket_write($this->users[$key][&#39;socket&#39;],$str,strlen($str));
 }
 }
 
 //用户退出
 function send2($k){
 $this->close($k);
 $ar[&#39;type&#39;]=&#39;rmove&#39;;
 $ar[&#39;nrong&#39;]=$k;
 $this->send1(false,$ar,&#39;all&#39;);
 }
 
 function e($str){
 //$path=dirname(__FILE__).&#39;/log.txt&#39;;
 $str=$str."\n";
 //error_log($str,3,$path);
 echo iconv(&#39;utf-8&#39;,&#39;gbk//IGNORE&#39;,$str);
 }
}
?>


很多童鞋反应用我的源码项目还是报错,不能运行,说下详细安装部署步骤。

首先把下载下来的源代码解压放到web目录下,比如我的就是applications/Xampp/xamppfiles/htdocs/phpb/websocket,

php+webSoket implementiert Chatroom-Beispielcode

路径

然后使用命令行工具cd进这个目录,运行命令:


php websocket.php


运行效果图:

php+webSoket implementiert Chatroom-Beispielcode

命令行操作

接着打开Apache服务器,在浏览器访问http://localhost/phpb/websocket/client.html

运行效果图:

php+webSoket implementiert Chatroom-Beispielcode

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHP中文网。

更多php+webSoket implementiert Chatroom-Beispielcode相关文章请关注PHP中文网!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn