English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Detailed annotations on the use of WebSocket in PHP for reference.
Server side:
<?php //Send user join //send1Send message //send2Logout error_reporting(E_ALL ^ E_NOTICE); ob_implicit_flush(); //file_put_contents('lz.text','123', FILE_APPEND); $sk=new Sock('127.0.0.1',8000); $sk->run(); class Sock{ public $sockets; public $users; public $master; private $sda=array();//Received data private $slen=array();//Total length of data private $sjen=array();//Length of received data private $ar=array();//Encryption key private $n=array(); public function __construct($address, $port){ $this-master=$this-WebSocket($address, $port);//resource(2, Socket) //Server listens var_dump("master:"); var_dump($this->master); $this->sockets = array($this->master);//array (size=1) 0 => resource(2, Socket) . Running two php scripts still like this //var_dump($this-sockets); //file_put_contents('lz.text',var_dump($this-sockets), FILE_APPEND); } function run(){ while(true){ $changes=$this->sockets;//$changes changes from multiple1, but $this->sockets are just stable+1; $write=NULL; $except=NULL; //1.The system call run select() in the given array of sockets with the specified timeout //2.No data received will remain in a blocked state, //3.If no client comes, the process will be blocked until a client visits, and then return1. //4.At this point, the changes returned are not the changes that were once. /*The special function of select: !!!!!!!!!! The initial state is an array(0=>resource(2, Socket)) 1,The initial state returns an array(0=>resource(2, Socket)) But socket_accept can get resource(3, Socket) 2,The initial state returns an array(0=>resource(2, Socket),1=>resource(3,Socket)). Client coming in is a resource(3,Socket). The returned data is a resource(3,Socket).!!! */ var_dump($changes); $rr=socket_select($changes,$write,$except,NULL); var_dump($changes); var_dump("---*---"); //exit; /* file_put_contents('lz.text',json_encode($changes), FILE_APPEND); file_put_contents('lz.text','-----', FILE_APPEND);*/ foreach($changes as $sock){ //Client connected to the host //$this->master is always a resource(2, Socket). It is equivalent to a cache. In two cases,1:Empty, causing the process to block.2:Store the just received client. if($sock==$this->master){ //---This is only used to store data. //After the socket socket has been created using socket_create(), it must be bound with socket_bind() name, telling the listening socket socket_listen(), this function will accept incoming connections, sockets. //Once connected successfully, a new socket resource will be returned !!A new socket resource!! which can be used for communication. If there are multiple connections on the socket, the first one will be used. //If there is no pending connection, socket_accept() will block until the connection becomes available. If a non-blocking socket is used and socket_set_blocking() or socket_set_nonblock() is called, an error will be returned. //The socket resource returned by socket_accept() cannot be used to accept new connections. The original listening socket is still open, and it can be reused. $client=socket_accept($this->master); //resource(3, Socket). Indicates that a request is accepted and a child link is created!! //var_dump($client); //exit; $key=uniqid(); $this->sockets[]=$client; $this->users[$key]=array( 'socket'=>$client, 'shou'=>false ); /* array (size=1) '57d607085f92a' => //$key array (size=2) 'socket' => resource(3, Socket) //$socket's behavior is the same, only distinguished by $key 'shou' => boolean false */ // file_put_contents('lz.text',json_encode($this->users), FILE_APPEND); }else{ //---Information is sent between the server and the client here $len=0; $buffer=''; do{ /* int socket_recv ( resource socket, string &buf, int len, int flags ) resource socket is the generated socket string &buf is the receiving buffer int len is the length you intend to receive int flags is a flag 0x1 Data should be sent out-of-band, where out-of-band data refers to TCP urgent data 0x2 Copy useful data to the buffer but do not delete it from the system buffer. 0x4 Do not route the packet out. The above three are exactly the same as defined in the sock.h file 0x8 Data is recorded completely 0x100 Data is processed completely */ $l=socket_recv($sock,$buf,1000,0);//The process of retrieving data is slow, and data needs to be retrieved one by one, and the length of each buf needs to be calculated to ensure that the total length does not exceed the set value. //var_dump($l); // exit; // file_put_contents('lz.text','socket_recv', FILE_APPEND); $len+$=l; $buffer.=$buf; }1000); $k = $this->search($sock);//According to sock return key value if($len<7){ //The message sent is too short, the system determines a break, and the connection is disconnected. $this->send2($k);//User logout.1Close the socket corresponding to the $key value, delete this key record, and rearrange the sockets array object. //2 continue; } if(!$this->users[$k]['shou']){//Determine if the handshake field of the user is true? Otherwise, rehandshake. $this->woshou($k, $buffer); //file_put_contents('lz.text', 'woshou', FILE_APPEND); }else{ //If the user has already handshaked, then communicate with the user. Finally, it's time to send messages! $buffer = $this->uncode($buffer, $k); //$mask = array(); if($buffer == false){ continue; } //var_dump($bufffer); //exit; $this->send($k, $buffer); } } } } } function close($k){ socket_close($this->users[$k]['socket']); unset($this->users[$k]); $this->sockets = array($this->master); foreach($this->users as $v){ $this->sockets[] = $v['socket']; } $this->e("key:$k close"); } function search($sock){ foreach ($this->users as $k=>$v){ if($sock == $v['socket']) return $k; } return false; } function WebSocket($address, $port){ //Server listens //Create and return a socket resource, also known as a communication node. A typical network connection consists of 2 A socket is composed of, one running on the client side, and the other on the server side. //Protocol, type, specific protocol $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); //resource(2, Socket) //file_put_contents('lz.text', $server, FILE_APPEND);//The supplied resource is not a valid stream resource //Return bool. Socket resource, protocol level, available socket option, value. $r = socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);//boolean true //Binding address to socket. This operation must be done before using socket_connect() or socket_listen() to establish a connection. $r2=socket_bind($server, $address, $port);//boolean true //The socket socket has been created using socket_create() and bounded with socket_bind(). It can tell the listening socket which incoming connection. $r3=socket_listen($server);//boolean true $this->e('Server Started : '.date('Y-m-d H:i:s')); $this->e('Listening on : '.$address.' port '.$port); return $server; } function woshou($k, $buffer){ //Handle the received buffer and return the handshake!! $buf = substr($buffer, strpos($buffer, '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 . \ \n\r\n"; socket_write($this->users[$k]['socket'], $new_message, strlen($new_message));//sokcet, buffer (buffer area), length $this-return true; function uncode($str,$key){ } Return compiled //$mask = array(); $data = ''; $msg = unpack('H', ',$str);*unpack() function unpacks data from a binary string.//$head = substr($msg[ ],0,1if ($head == '2); ' && !isset($this81>slen[$key])) {-)=substr($msg[ $len=hexdec($len);1,2$s =2); $msg[ hexdec() function converts hexadecimal to decimal.1,2$s =2else if(substr($msg[ $len=hexdec($len);1,4$s =4); $msg[//} =substr($msg[1$mask[] = hexdec(substr($msg[1,4); )=='ff'){1,2$s =2$len=substr($msg[ $len=hexdec($len);1,4$s =16); $msg[ =substr($msg[1$mask[] = hexdec(substr($msg[1,16); } ],1,4$s =2)); ],1,6$s =2)); ],1,8$s =2)); ],1,10$s =2)); $n=0; 12; 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]); function code($msg){ } } Compile //$frame = array(); $frame[0] = ' $len = strlen($msg);81'; if($len < ] = $len< 126){ $frame[1;'0'.dechex($len):dechex($len);16?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; } $frame[2] = $this->ord_hex($msg); $data = implode('',$frame); return pack("H*", $data); } function ord_hex($data) { $msg = ''; $l = strlen($data); for ($i= 0; $i<$l; $i++) { $msg .= dechex(ord($data{$i})); } return $msg; } //User joins function send($k,$msg){ parse_str($msg,$g);//Parse the query string into variables $ar=array(); if($g['type']=='add'){ $this->users[$k]['name']=$g['ming']; $ar['type']='add'; $ar['name']=$g['ming']; $key='all'; }else{ $ar['nrong']=$g['nr']; $key=$g['key']; } $this->send1($k,$ar,$key); } function getusers(){ $ar=array(); foreach($this->users as $k=>$v){ $ar[]=array('code'=>$k,'name'=>$v['name']); } return $ar; } //$k sender's code $key receiver's code function send1($k,$ar,$key='all'){ $ar['code1']=$key; $ar['code']=$k; $ar['time']=date('m-d H:i:s'); $str = $this->code(json_encode($ar)); if($key=='all'){ $users=$this->users; if($ar['type']=='add'){ $ar['type']='add'; $ar['users']=$this->getusers(); $str1 = $this->code(json_encode($ar)); socket_write($users[$k]['socket'],$str1,strlen($str1));//Sender unset($users[$k]); } foreach($users as $v){ socket_write($v['socket'],$str,strlen($str));//Receiver } }else{ socket_write($this->users[$k]['socket'],$str,strlen($str));//Sender socket_write($this->users[$key]['socket'],$str,strlen($str));//Receiver } } //User logout function send2($k){ $this->close($k); $ar['type']='remove'; $ar['nrong']=$k; $this->send1(false,$ar,'all'); } function e($str){ //$path=dirname(__FILE__).'/log.txt'; $str=$str."\n"; //3 echo iconv('utf-8','gbk//IGNORE,$str); } } ?>
Client:
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"/> <title>HTML5 websocket web chat room 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-index: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-ecoration: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> <div id="ltian"> <div id="us" class="jb"></div> <div id="ct"></div> <a href="javascript:;" class="qp" onClick="this.parentNode.children[1].innerHTML=''">Clear Screen</a> </div> <div class="rin"> <button id="sd">Send</button> <span><img src="http://www.yxsss.com/ui/sk/t.png" title="[#1#]" id="imgbq"><img src="http://www.yxsss.com/ui/sk/e.png" title="[#2#]"><form><input type="file" title="[#2#]" id="upimg"></form></span> <p><input id="nrong"></p> </div> <div id="ems"><p></p><p class="tc"></p></div> <script> if(typeof(WebSocket)=='undefined'){ alert('Your browser does not support WebSocket, it is recommended to use Google Chrome or Mozilla Firefox'); } </script> <script src="http://www.yxsss.com/ui/p/a.js" type="text/javascript"></script> <script> (function(){ var key='all',mkey; var users={}; var url='ws://127.0.0.1:8000'; var so=false,n=false; var lus=A.$('us'),lct=A.$('ct'); function st(){ n=prompt('Please choose a catchy name for yourself:'); n=n.substr(0,16); if(!n){ return ; } so=new WebSocket(url); so.onopen=function(){ if(so.readyState==1){ alert('888'); so.send('type=add&ming='+n); } } so.onclose=function(){ so=false; lct.appendChild(A.$$('<p class="c2">Exit Chat Room</p>')); } so.onmessage=function(msg){ eval('var da='+msg.data); var obj=false,c=false; if(da.type=='add'){ var obj=A.$('<p>'+da.name+'</p)> lus.appendChild(obj); cuser(obj,da.code); obj=A.$('<p><span>['+da.time+']<}}/span>Welcome<a>+da.name+'</a>Join</p)> c=da.code; }else if(da.type=='madd'){ mkey=da.code; da.users.unshift({'code':'all','name':'Everyone'}); for(var i=0;i<da.users.length;i++){ var obj=A.$('<p>'+da.users[i].name+'</p)> lus.appendChild(obj); if(mkey!=da.users[i].code){ cuser(obj,da.users[i].code); }else{ obj.className='my'; document.title=da.users[i].name; } } obj=A.$('<p><span>['+da.time+']<}}/span>Welcome'+da.name+'Join</p)> users.all.className='ck'; } if(obj==false){ if(da.type=='rmove'){ var obj=A.$('<p class="c2><span>['+da.time+']<}}/span>"+users[da.nrong].innerHTML+'Logout from chat room</p)> lct.appendChild(obj); users[da.nrong].del(); delete users[da.nrong]; }else{ da.nrong=da.nrong.replace(/{\\(\d+})/g,function(a,b){ return '<img src="sk/'+b+'.gif">'; }).replace(/^data:image\/png;base64\,.{50,}$/i,function(a){ return '<img src="'+a+'">'; }); //da.code is the code of the sender if(da.code1==mkey){ obj=A.$('<p class="c3><span>['+da.time+']<}}/span><a>'+users[da.code].innerHTML+'</a>say to me:+da.nrong+'</p)> c=da.code; }else if(da.code==mkey){ if(da.code1!='all') obj=A.$('<p class="c3><span>['+da.time+']<}}/span>I said to<a>+users[da.code1].innerHTML+'</a>say:+da.nrong+'</p)> else obj=A.$('<p><span>['+da.time+']<}}/span>I said to<a>+users[da.code1].innerHTML+'</a>say:+da.nrong+'</p)> c=da.code1; }else if(da.code==false){ obj=A.$('<p><span>['+da.time+']<}}/span>"+da.nrong+'</p)> }else if(da.code1){ obj=A.$('<p><span>['+da.time+']<}}/span><a>'+users[da.code].innerHTML+'</>对'+users[da.code1].innerHTML+':'+da.nrong+'</p)> 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.$('sd').onclick=function(){ if(!so){ return st(); } var da=A.$('nrong').value.trim(); if(da==''){ alert('内容不能为空'); return false; } A.$('nrong').value=''; so.send('nr='+esc(da)+'&key='+key); } A.$('nrong').onkeydown=function(e){ var e=e||event; if(e.keyCode==13){ A.$('sd').onclick(); } } function esc(da){ da=da.replace(/</g,'<').replace(/>/g,'>').replace(/\"/g,'"'); return encodeURIComponent(da); } function cuser(t,code){ users[code]=t; t.onclick=function(){ t.parentNode.children.rcss('ck',''); t.rcss('','ck'); key=code; } } A.$('ltian').style.height=(document.documentElement.clientHeight - 70);+'px'; st(); var bq=A.$('imgbq'),ems=A.$('ems'); var l=80,r=4,c=5,s=0,p=Math.ceil(l/(r*c)); var pt='sk/'; bq.onclick=function(e){ var e=e||event; if(!so){ return st(); } ems.style.display='block'; document.onclick=function(){ gb(); } ct(); try{e.stopPropagation();}catch(o){} } for(var i=0;i<p;i++){ var a=A.$('<a href="javascript:;">'+(i+1)+'</a)> ems.children[1.appendChild(a); ef(a,i);}} } ems.children[1].children[0].className='ck'; function ct(){ var wz=bq.weiz(); with(ems.style){ top=wz.y-242+'px'; left=wz.x+bq.offsetWidth-235+'px'; } } function ef(t,i){ t.onclick=function(e){ var e=e||event; s=i*r*c; ems.children[0].innerHTML=''; hh(); this.parentNode.children.rcss('ck',''); this.rcss('','ck'); 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.$$('<img src="'+pt+i+'.gif">'); hh1(a,i); ems.children[0].appendChild(a); } ct(); } function hh1(t,i){ t.onclick=function(e){ var e=e||event; A.$('nrong').value+='{\\'+i+}; if(!e.ctrlKey){ gb(); } try{e.stopPropagation();}catch(o){} } } function gb(){ ems.style.display=''; A.$('nrong').focus(); document.onclick=''; } hh(); A.on(window,'resize',function(){ A.$('ltian').style.height=(document.documentElement.clientHeight - 70);+'px'; ct(); }); var fimg=A.$('upimg'); var img=new Image(); var dw=400,dh=300; A.on(fimg,'change',function(ev){ if(!so){ st(); return false; } if(key=='all'){ alert('Due to resource limitations, sending images can only be done in private chat'); return false; } var f=ev.target.files[0]; if(f.type.match('image.*')){ var r = new FileReader(); r.onload = function(e){ img.setAttribute('src',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.$$('canvas'); var ct = rc.getContext('2d'); rc.width=iw; rc.height=ih; ct.drawImage(img,0,0,iw,ih); var da=rc.toDataURL(); so.send('nr='+esc(da)+'&key='+key); } })(); </script> </body> </html>
That's all for this article. Hope it helps everyone's learning and also hope everyone will support the Yell Tutorial.
Declaration: The content of this article is from the Internet, the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, does not edit the content manually, and does not assume relevant legal liability. If you find any content suspected of copyright infringement, please send an email to notice#w3Please send an email to codebox.com (replace # with @ when sending an email) to report any violations, and provide relevant evidence. Once verified, this site will immediately delete the content suspected of infringement.