先给你们开一个原始的websocket的链接使用范例javascript
<?php /* * recv是从套接口接收数据,也就是拿过来,可是不知道是什么 * read是读取拿过来的数据,就是要知道recv过来的是什么 * write是向套接口写数据,可是只是写,并无发送出去 * send是write以后,将数据传输到套接口,以便其余人recv以后read */ //设置一些基本的变量 $host="192.168.1.68"; //连接ip $port='1423'; //端口号 //设置超时时间 set_time_limit(0); //建立一个Socket $socket=socket_create(AF_INET,SOCK_STREAM,0) or die("Could not create socket\n");//绑定Socket到端口 $result=socket_bind($socket,$host,$port) or die("Could not bind to socket\n");//开始监听连接 $result=socket_listen($socket,3) or die("Could not setup socket listener\n");//accept in coming connections //另外一个Socket来处理通讯 $spawn=socket_accept($socket) or die("Could not accept in coming connection\n");//得到客户端的输入 $input=socket_read($spawn,1024) or die("Could not read input\n");//清空输入字符串 $input=trim($input);//处理客户端输入并返回结果 $output=strrev($input)."\n"; socket_write($spawn,$output,strlen($output)) or die("Could not write out put\n");//关闭 socket_close($spawn); socket_close($socket);
ob_implicit_flush(); //将打开或关闭绝对(隐式)刷送。绝对(隐式)刷送将致使在每次输出调用后有一次刷送操做 //地址与接口,即建立socket时须要服务器的IP和端口 $sk = new WebSocket('127.0.0.1',1208); //对建立的socket循环进行监听,处理数据 $sk->Run(); class WebSocket{ public $sockets; //socket的链接池,即client链接进来的socket标志 public $users; //全部client链接进来的信息,包括socket、client名字等 public $master; //socket的resource,即前期初始化socket时返回的socket资源 /* * 构造函数 * 实例化的时候,自动运行 * */ public function __construct($address, $port){ //建立socket并把保存socket资源在$this->master $this->master = $this->WebSocket($address, $port); //$socket //建立socket链接池 链接的用户 $this->sockets = array($this->master); //$clients } /* * 传相应的IP与端口进行建立socket操做 * 链接socket 建立tcp socket * */ function WebSocket($address,$port){ $server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);//1表示接受全部的数据包 socket_bind($server, $address, $port); socket_listen($server); //监听端口 return $server; } //对建立的socket循环进行监听,处理数据 public function Run(){ $null = NULL; //死循环,直到socket断开 while(true){ $changes = $this->sockets; $socket = $this->master; /* 这个函数是同时接受多个链接的关键,个人理解它是为了阻塞程序继续往下执行。 socket_select ($sockets, $write = NULL, $except = NULL, NULL, timeout); $sockets能够理解为一个数组,这个数组中存放的是文件描述符。当它有变化(就是有新消息到或者有客户端链接/断开)时,socket_select函数才会返回,继续往下执行。 $write是监听是否有客户端写数据,传入NULL是不关心是否有写变化。 $except是$sockets里面要被排除的元素,传入NULL是”监听”所有。 最后一个参数是超时时间 若是为0:则当即结束 若是为n>1: 则最多在n秒后结束,如遇某一个链接有新动态,则提早返回 若是为null:如遇某一个链接有新动态,则返回 */ /* * @socket_select 第四个参数 * 第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,必定等到监视文件描述符集合中某个文件描述符发生变化为止; * 第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,无论文件描述符是否有变化,都马上返回继续执行,文件无变化返回0,有变化返回一个正值; * 第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间以内有事件到来就返回了,不然在超时后无论怎样必定返回,返回值同上述。 */ socket_select($changes,$null,$null,NULL); //socket_select($changed, $null, $null, 0, 10); /* * 若是有新的连接进来 * */ //若是有新的链接 if (in_array($socket, $changes)) { //接受一个socket链接 $socket_new = socket_accept($socket); //给新链接进来的socket一个惟一的ID $changes = $this->sockets[] = $socket_new; //将新链接进来的socket存进链接池 //经过socket获取数据执行handshake $header = socket_read($socket_new, 1024); $this->perform_handshaking($header, $socket_new, '127.0.0.1', '1208'); //握手协议 //获取client ip 编码json数据,并发送通知 /* * 获取远程套接口的名字,包括它的IP和端口。 * */ socket_getpeername($socket_new, $ip); $response = $this->mask(json_encode(array('type'=>'system', 'message'=>'ip:'.$ip.' 已链接!'))); $this->send_message($response); $found_socket = array_search($socket, $changes); unset($changes[$found_socket]); } //轮询 每一个client socket 链接 发送数据 foreach ($changes as $key=>$changed_socket) { //若是有client数据发送过来 /* * @socket_recv ( resource $socket , string &$buf , int $len , int $flags ) * 从已链接的socket接收数据 * */ while(socket_recv($changed_socket, $buf, 1024, 0) >= 1) { //解码发送过来的数据 $received_text = $this->unmask($buf); $tst_msg = json_decode($received_text); $user_name = $tst_msg->name; $user_message = $tst_msg->message; $this->writeLog('消息:', json_decode($received_text, true)); //把消息发送回全部链接的 client 上去 $response_text = $this->mask(json_encode(array('type'=>'usermsg', 'name'=>$user_name, 'message'=>$user_message))); $this->send_message($response_text); break 2; } //检查offline的client $buf = @socket_read($changed_socket, 1024, PHP_NORMAL_READ); if ($buf === false) { $found_socket = array_search($changed_socket, $changes); /* * 获取远程套接口的名字,包括它的IP和端口。 * */ socket_getpeername($changed_socket, $ip); unset($changes[$found_socket]); $response = $this->mask(json_encode(array('type'=>'system', 'message'=>'ip:'.$ip.' 已断开!'))); $this->send_message($response); } } } /* * 在监听外面关闭socket连接 * 关闭监听的socket * */ socket_close($this->master); } /* * 对发送的数据进行编码 * */ public function mask($text) { $b1 = 0x80 | (0x1 & 0x0f); $length = strlen($text); if ($length <= 125) { $header = pack('CC', $b1, $length); } elseif ($length > 125 && $length < 65536) { $header = pack('CCn', $b1, 126, $length); } elseif ($length >= 65536) { $header = pack('CCNN', $b1, 127, $length); } return $header.$text; } /* * 对接受来的数据进行解码 * */ public function unmask($text) { /* * @ ord * 函数返回字符串的首个字符的 ASCII 值。 * */ $length = ord($text[1]) & 127; if ($length == 126) { $masks = substr($text, 4, 4); $data = substr($text, 8); } elseif ($length == 127) { $masks = substr($text, 10, 4); $data = substr($text, 14); } else { $masks = substr($text, 2, 4); $data = substr($text, 6); } $text = ""; $len = strlen($data); for ($i = 0; $i < $len; $i++) { $text .= $data[$i] ^ $masks[$i%4]; } return $text; } //握手的逻辑 public function perform_handshaking($receved_header,$client_conn, $host, $port) { $headers = array(); $lines = preg_split("/rn/", $receved_header); foreach($lines as $line) { $line = chop($line); if(preg_match('/A(S ): (.*)z/', $line, $matches)) { $headers[$matches[1]] = $matches[2]; } } if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $receved_header, $match)) { $key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true)); $upgrade = "HTTP/1.1 101 Switching Protocol\r\n" . "Upgrade: websocket\r\n" . "Connection: Upgrade\r\n" . "Sec-WebSocket-Accept: ".$key."\r\n\r\n"; socket_write($client_conn,$upgrade,strlen($upgrade)); } return true; } /* * @socket_ * recv是从套接口接收数据,也就是拿过来,可是不知道是什么 * read是读取拿过来的数据,就是要知道recv过来的是什么 * write是向套接口写数据,可是只是写,并无发送出去 * send是write以后,将数据传输到套接口,以便其余人recv以后read * 发送消息 * */ //发送消息的方法 public function send_message($msg) { //global $this->sockets; foreach($this->sockets as $changed_socket) { @socket_write($changed_socket,$msg,strlen($msg)); } return true; } /* * 将发送的消息记录到log中 * var_export 输出或返回一个变量的字符串表示 * */ //打印本地Err public function writeLog($str, $arr) { $cont = var_export($arr, true)."\n"; $time = date('Y-m-d H:i:s',time()); file_put_contents('./WebSocket/mylog', $time, FILE_APPEND); file_put_contents('./WebSocket/mylog', $str, FILE_APPEND); file_put_contents('./WebSocket/mylog', $cont."\n", FILE_APPEND); echo $cont; } }
//客户端的实现代码php
<!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;} .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=''">清屏</a> </div> <div class="rin"> <button id="sd">发送</button> <p><input id="message"></p> </div> <div id="ems"><p></p><p class="tc"></p></div> </body> <script src="http://www.study.com/Reids/jquery-1.11.3.min.js"></script> <script> //console.log(Math.ceil((Math.random()*100000)+(Math.random()*100000))); if(typeof(WebSocket)=='undefined'){ alert('你的浏览器不支持 WebSocket ,推荐使用Google Chrome 或者 Mozilla Firefox'); } </script> <script> $(function(){ var websocket; var name = Math.ceil((Math.random()*100000)+(Math.random()*100000)); if(window.WebSocket) { websocket = new WebSocket('ws://'+ip+':1208/WebSocket/Class/WebSocket.php'); //链接创建 websocket.onopen = function(evevt) { console.log("WebSocket已链接!"); $('#ct').append('<p><span>你们好,WebSocket已链接!</span></p>'); } //收到消息 websocket.onmessage = function(event) { var msg = JSON.parse(event.data); //解析收到的json消息数据 console.log(msg); if(msg.type == 'system'){ $('#ct').append('<p><span>'+msg.message+'</span></p>'); console.log(msg.message); }else if(msg.type == 'usermsg'){ $('#ct').append('<p><span>'+msg.message+'</span><a>用户:'+msg.name+'</a></p>'); console.log(msg.message); } } //发生错误 websocket.onerror = function(event) { console.log("WebSocket链接出错!"+ event.data); $('#ct').append('<p><span>WebSocket Error ' + event.data + '</span></p>'); } //链接关闭 websocket.onclose = function(event) { console.log('WebSocket已断开链接'); $('#ct').append('<p><span>WebSocket已关闭!</span></p>'); } /* 发送消息 */ function send() { var message = $('#message').val(); if(!message) { alert('发送消息不能为空!'); return false; } var msg = { message: message, name: name }; try{ websocket.send(JSON.stringify(msg)); $('#message').val(''); //websocket.send(msg); } catch(ex) { console.log(ex); } } //按下enter键发送消息 $(window).keydown(function(event) { if(event.keyCode == 13) { send(); } }); //点发送按钮发送消息 $('#sd').bind('click',function() { send(); }); } else { console.log('你的浏览器不支持Web Socket!'); } }) </script>