服务端

websocket.php 文件
<?php
$socket1 = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);
socket_bind($socket1,"0.0.0.0",6003);
socket_listen($socket1,3);
$clients = array($socket1);
while(true){//socket_select对读写套子节的数字是引用,为了保证clients不被改变,拷贝一份。$read=$clients;$write = null;$except= null;if( socket_select($read , $write,$except,null) > 0){foreach($read as $socket_item){if ($socket_item == $socket1){if(!$socket_client = socket_accept($socket_item)) continue;$hello = @socket_read($socket_client, 1024);if ($hello == false){socket_close($socket_client);continue;}handshake($hello,$socket_client);socket_getpeername($socket_client,$ip,$port);//区分前端客户端 消息 还是 php客户端消息if(preg_match('/Sec-WebSocket-Key: (.*)\r\n/',$hello)){$clients["ws:".$ip." :". $port] =$socket_client;}else{$clients["cl:".$ip." :". $port] =$socket_client;}echo "new client[".$ip.":".$port. "]\n";echo "hello msg [" .$hello."]\n";}else{$result = @socket_recv($socket_item,$msg,1024,0);/*//调试用echo "result :\n";var_dump($result);echo "mag1 :\n";var_dump($msg);echo "mag2 decode :\n";var_dump(decode($msg));echo "mag3 ord-decode :\n";var_dump(ord(decode($msg)));*///$result === 8 && ord(decode($msg)) == 3 前端客户端 页面关闭条件//$result === false && $msg === NULL php 客户端 关闭条件if($result === 0 || ($result === 8 && ord(decode($msg)) == 3) || ($result === false && $msg === NULL)){socket_close($socket_item);$key1=array_search($socket_item,$read);unset($read[$key1]);$key2=array_search($socket_item,$clients);unset($clients[$key2]);echo "client [$key2] is closed!\n";}else  if(!empty($result)){$id = search($socket_item,$clients);//前端客户端消息要解码  客户端消息不解码$web_msg = strpos($id,"ws") !== false ? decode($msg) : $msg;echo "client [".$id."] say: " .$web_msg."\n" ;foreach($clients as $client_socket) {//发给别人(除去监听和自己)if($client_socket != $socket1 ){$key2=array_search($client_socket,$clients);$broadcast =  strpos($key2,"ws") !== false ? encode($web_msg) :  $web_msg;if(false==@socket_write($client_socket,$broadcast,strlen($broadcast))){socket_close($client_socket);$key1=array_search($client_socket,$read);unset($read[$key1]);$key2=array_search($client_socket,$clients);unset($clients[$key2]);echo "otherclient [$key2] is closed!\n";}}}}if ($result === false) continue;}}}else{continue;}
}function handshake( $buffer,$client_socket){//截取Sec-WebSocket-Key的值并加密preg_match('/Sec-WebSocket-Key: (.*)\r\n/',$buffer,$match);$server_key = base64_encode(sha1($match[1]. '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));echo "client_key [".$match[1]."]\n";echo "server_key [". $server_key."]\n\n";if ($match[1] == '') return false;$handshake_msg = "HTTP/1.1 101 Switching Protocols\r\n";$handshake_msg .= "Upgrade: websocket\r\n";$handshake_msg .= "Sec-webSocket-Version: 13\r\n";$handshake_msg .= "Connection:Upgrade\r\n";//一定要切记  最后一行需要两个换行符  \r\n\r\n$handshake_msg .= "Sec-webSocket-Accept: " . $server_key . "\r\n\r\n";socket_write($client_socket ,$handshake_msg,strlen($handshake_msg));echo $handshake_msg;return true;
}function encode($buffer){$len =strlen($buffer);if ($len<=125) {return "\x81" . chr($len) . $buffer;} else if ($len<=65535) {return "\x81" . chr(126) . pack( "n", $len) . $buffer;} else {return "\x81" . chr(127) . pack("xxxxN", $len) . $buffer;}
}function decode( $buffer){$len = $masks = $data = $decoded = null;$len = ord($buffer[1]) & 127;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;
}function search($socket_client,$clients){$search = array_search($socket_client,$clients,true);if ($search === null) $search = false;return $search;
}

客户(两种)

一 、php 作为客户端 只负责发送信息 这里方便测试建了前端

index.html 文件

<head><meta charset="UTF-8">
</head><form action="http://localhost/" method="post"><input type="text" name="data" value=""><input type="submit" value="发送">
</form>

index.php 文件

<?php
header("Content-Type: text/html; charset=utf-8");
$host = '127.0.0.1';
$port = 6003;
$sock = null;
if (($sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP)) === false){//创建socketecho "socket_create(0) failed: reason: ".socket_strerror(socket_last_error()) . "\n";return;
}
if($con = socket_connect($sock, $host,$port) === false){echo 'connect fail' . "\n";return;
}else{echo ' connect success'. "\n";
}$client_msg="client202204281630";if(socket_write($sock, $client_msg,strlen($client_msg)) === false){echo 'fail to write '.socket_strerror(socket_last_error());
}else{echo 'client write success' .PHP_EOL. "\n";//读取服务端返回来的套接流信息
//    while(1){
//        if($serverback = @socket_read($sock, 1024)){
//            echo "server return message is: " .$serverback.PHP_EOL;
//        }
//        $date = date("Y-m-d H:i:s");
//
//        if(socket_write($sock, $date,strlen($date)) === false){
//            echo 'fail to write '.socket_strerror(socket_last_error());
//        }else{
//            echo "send msg : $date \n";
//        }
//
//        $serverback = socket_read($sock, 1024);
//        echo "serverback : \n";
//        var_dump($serverback);
//        sleep(1);
//    }sleep(1);$data = $_POST['data'];if(socket_write($sock, $data,strlen($data)) === false){echo 'data send fail : '.socket_strerror(socket_last_error());}else{echo 'data send success';}socket_close($sock);
}header("Location:index.html");
二 、前端websocket
var ws = new WebSocket("ws://127.0.0.1:6003");ws.onopen = function(evy){console.log("connection open....");}ws.onmessage = function(evt){console.log("recv msg:"+ evt.data);
}ws.onclose = function(){console.log("connection closed");
}

基础理解篇
service.php文件

<?php
/**AF_INET:  IPv4网络协议。TCP和UDP都可使用此协议。AF_INET6: IPv6网络协议。TCP和UDP都可使用此协议。AF_UNIX:  本地通讯协议。具有高性能和低成本的IPC(进程间通讯)SOCK_STREAMTCP 协议套接宇。SOCK_DGRAMUDP  协设套接字SOL_TCP:TCP协议。SOL_UDP:UDP协议。
*/
$socket1 = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) ;socket_bind($socket1,'192.168.197.128','6003');socket_listen($socket1,500);$clients = array($socket1); /* 个人理解 此处$socket1 为客户端的连接资源  以下 $socket2 为客户端的消息资源*/while (true){/**socket_select对读写套子节的数字是引用,为了保证clients不被改变,拷贝一份。$read=$clients;*/$read = $clients; // 个人理解 $clients 里存储 连接资源 和 消息资源$write = null;$except = null;/* echo "clients_num:".count($clients) ."\r\n";echo "read_num:".count($read) ."\r\n";echo "1111 var_dump clients:"."\r\n";var_dump($clients);echo "1111 var_dump read:"."\r\n";var_dump($read);echo "\r\n";*//**当read发生变化,说明:有客户端发生连接操作了,$write/$except均为null,所以只监测read数组的变化第4个参数:null,表示阻塞;socket_select()会阻塞—直到发现变化,0,表示为非阻塞,调用socket_select()后,立即返回,然后继续调用socket_select()不断循环返回值是修改后的数组中包含的套接字资源的数量个人理解:socket_select 判断 $read 里的各种资源 有没有变化 ? 有的变化 说明资源活跃  留在 $read (一般只存在一种资源 消息 或 连接);没有的 删除掉;助解:https://blog.csdn.net/Im_KK/article/details/45033533*/if(socket_select($read,$write,$except,null) > 0){/* echo "in_array var_dump socket1:"."\r\n";var_dump($socket1);echo "\r\n";echo "in_array var_dump read:"."\r\n";var_dump($read);echo "\r\n";*/// 经过 socket_select  $read 里都是有的变化 活跃资源   当连接资源 $socket1 在 $read 存在 说明 有人连接客户端了if(in_array($socket1,$read)){$socket2 = socket_accept($socket1); // socket接受后  成为存消息的资源 $socket2$clients[] = $socket2;socket_write($socket2,"yuo are ".(count($clients)-1)." client\r\n");socket_getpeername($socket2,$ip,$port);echo "新连接客户端 [ip:{$ip} port:{$port}]\r\n";$key = array_search($socket1,$read);unset($read[$key]); //删除 $read中的 连接资源 ;因为下面要进行 消息发生 $read中 应该是消息资源}/*echo "clients_num:".count($clients) ."\r\n";echo "read_num:".count($read) ."\r\n";echo "2222 var_dump clients:"."\r\n";var_dump($clients);echo "222 var_dump read:"."\r\n";var_dump($read);*/if(count($read) > 0){foreach ($read as $socket_item){$msg = socket_read($socket_item,1024);echo "接收到消息:{$msg} \r\n";socket_write($socket_item,"接收到消息:{$msg}",strlen($msg));foreach ($clients as $client_socket){//$clients 中的 连接资源没有被删除 ; 不给 连接资源$socket1发消息// 也不给 自己发消息 $socket_item 当前活跃的消息资源if($socket1 != $client_socket && $socket_item != $client_socket){sleep(1);socket_write($client_socket,$msg,strlen($msg));}}}}/*echo "\r\n";echo "\r\n";echo "\r\n";*/}
}

websocket封装成类

<?php
class SocketServer
{private $socket;private $readGroup = array();                                 //保存读的套接字private $writeGroup = array();                                    //保存写的套接字private $except = array();private $mcrypt_key = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';    //websocket协议中用于加密的字符串private $test = false;/***初始化*@param String $host ip地址*@param int $port 端口*@param int $backlog 最大连接数*ws://121.40.165.18:8800*/public function __construct($host = '192.168.197.128',$port = '8081', $backlog = 10){$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die('socket创建失败');socket_bind($this->socket,$host,$port);socket_listen($this->socket,$backlog);$this->readGroup[] = $this->socket;   //将所有套接字存于该数组echo PHP_EOL;echo "  readGroup:".PHP_EOL;echo PHP_EOL;var_dump($this->readGroup);}public function start(){while(true){//socket_select会移除了数组元素,所以必须用临时变量重置数组$socketArr = $this->readGroup;  //阻塞,直到捕获到变化socket_select($socketArr, $this->writeGroup , $this->except , 3600);//遍历读的套接数组foreach($socketArr as $socket){echo PHP_EOL;echo "  socketArr:".PHP_EOL;echo PHP_EOL;var_dump($socketArr);echo PHP_EOL;echo "  socket:".PHP_EOL;echo PHP_EOL;var_dump($socket);//如果是当前服务器的监听连接if($this->socket == $socket){$client = socket_accept($this->socket);echo PHP_EOL;echo "client:".PHP_EOL;echo PHP_EOL;var_dump($client);//添加客户端套接字$this->add_client($client);                 }else{//获取客户端发送来的信息$msg = @socket_read($socket,1024);echo PHP_EOL;echo "msg:".PHP_EOL;echo PHP_EOL;var_dump($msg);//如果检测到客户端发送的是握手协议,则向客户端发送握手协议if(empty($msg)){continue;}else if(preg_match('/Sec-WebSocket-Key: (.*)\r\n/', $msg,$matches))                     {echo PHP_EOL;echo "matches:".PHP_EOL;echo PHP_EOL;var_dump($matches);$upgrade = $this->createHandShake($matches[1]);@socket_write($socket,$upgrade,strlen($upgrade));echo PHP_EOL;echo "  socketArr222:".PHP_EOL;echo PHP_EOL;var_dump($socketArr);}else{//解码客户端发送的消息$client_info = $this->decodeMsg($msg);echo PHP_EOL;echo "client_info:".PHP_EOL;echo PHP_EOL;echo $client_info.PHP_EOL;//向其他客户端进行广播$this->send_to_other($client_info,$socket);}}}}}/***@param resource $client 客户端的套接字*/private function add_client($client){$this->readGroup[] = $client;}/***发送到指定客户端*@param resource $socket 客户端套接字*@param String $data 要发送的消息*/private function send_to_one($socket,$data){//先对信息进行编码$data = $this->encodeMsg($data);socket_write($socket, $data);}/***广播至所有客户端*@param String $data*/private function send_to_all($data){$writeGroup = $this->readGroup;unset($writeGroup[0]);               //除去服务器自身$data = $this->encodeMsg($data);foreach($writeGroup as $socket){socket_write($socket, $data);}}/***广播至除发送消息外的客户端*@param String $data*@param resource $client */private function send_to_other($data,$client){$writeGroup = $this->readGroup;unset($writeGroup[0]);               //除去服务器自身$data = $this->encodeMsg($data);foreach($writeGroup as $socket){if($socket != $client){socket_write($socket, $data);}}}/***计算返回客户端的协议*@param String $msg 客户端发送过来的协议*@return String $upgrade 返回给客户端的协议*/private function createHandShake($client_key){$key = base64_encode(sha1($client_key.$this->mcrypt_key,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";        //结尾一定要两个\r\n\r\nreturn $upgrade;}/***解码客户端发送过来的信息*@param binary $buffer 客户端传来的信息*@return String $decoded 解码后的字符串*/private function decodeMsg($buffer){$len = $masks = $data = $decoded = null;$len = ord($buffer[1]) & 127;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 encodeMsg($msg){$head = str_split($msg, 125);if (count($head) == 1){return "\x81" . chr(strlen($head[0])) . $head[0];}$info = "";foreach ($head as $value){$info .= "\x81" . chr(strlen($value)) . $value;}return $info;}public function __destruct(){socket_unbind($this->socket);}
}$socketServer = new SocketServer();
$socketServer->start();

php websocket通信相关推荐

  1. Python3+WebSockets实现WebSocket通信

    一.说明 1.1 背景说明 前端时间同事说云平台通信使用了一个websocket的东西,今天抽空来看一下具体是怎么个通信过程. 从形式上看,websocket是一个应用层协议,socket是数据链路层 ...

  2. C#(SuperWebSocket)与websocket通信

    原文:C#(SuperWebSocket)与websocket通信 客户端代码 点击可以查看一些关于websocket的介绍 1 <!DOCTYPE html> 2 <html> ...

  3. WebSocket 通信原理和详细使用(十六)

    今天我们详细分析WebSocket 通信原理和使用 一.什么是 WebSocket ? WebSocket --一种在 2011 年被互联网工程任务组( IETF )标准化的协议.WebSocket ...

  4. 《 Socket.IO》 解决 WebSocket 通信

    大家好呀,我是小菜~ 本文主要介绍 Socket.IO 微信公众号已开启,小菜良记,没关注的同学们记得关注哦! 在介绍 Socket.IO 之前, 我们先考虑一个问题, 如果这个时候有个需求, 类似实 ...

  5. 微信小程序 WebSocket 通信 —— 在线聊天

    在Node栏目就讲到了Socket通信的内容,使用Node实现Socke通信,还使用两个流行的WebSocket 库,ws 和 socket.io,在小程序中的WebSocket接口和HTML5的We ...

  6. 请使用netty框架实现高效稳定的websocket通信

    Netty 是一个异步事件驱动的网络应用框架,提供了一种高效稳定的方法来实现 WebSocket 通信. 要使用 Netty 实现 WebSocket 通信,需要执行以下步骤: 创建一个新的 Nett ...

  7. 在vue中webSocket通信

    1.简单介绍 基于webSocket通信的库主要有 socket.io,SockJS,这次用的是 SockJS. 2.前提 这里我们使用sockjs-client.stomjs这两个模块,要实现web ...

  8. 基于asp.netCoreWebApi的webSocket通信示例(net6)

    背景: 在阿里云服务器中搭建了常规的tcp server服务(基于.net framework 4.0).用以实现远程控制家里的鱼缸灯,办公室的电脑开关机等功能.客户端采用PC桌面端和微信小程序端. ...

  9. Netty作为服务端的websocket通信

    http协议是无状态的,因此导致客户端每次通信都需要携带标识(session)给服务端,以此来识别是哪个客户端发送过来的信息.但是当服务端主动推送给客户端时就无法实现了,因为服务端不知道客户端在哪,此 ...

  10. 基于H5的webSocket通信

    基于H5的webSocket通信 这里主要以一个简单例子来做说明 创建一个主服务器 1.创建一个主服务器 主服务器创建逻辑过程 /*1.通过ws模块来创建服务器2.服务器连接客户端---(给客户端编号 ...

最新文章

  1. 大工18秋《c c 语言程序设计》,大工18秋《毕业论文(设计)写作指导》在线测试1.txt...
  2. 基于消息的分布式架构设计
  3. python插件安装错误解决办法之SyntaxError: from __future__ imports must occur at the beginning of the file原因
  4. 【NLP】fastText词向量与文本分类工具
  5. php response body,数据库读写没问题,response body部分空
  6. html5 实现坦克大战,HTML5实现坦克大战(一)
  7. BestCoder Round #4 前两题 hdu 4931 4932
  8. IdentityServer4 4.x版本 配置Scope的正确姿势
  9. 32位hex转浮点 python_python——int()、hex()、oct()、bin()、float()数值类型转换函数
  10. redis集群scan_Redis scan命令的一次坑
  11. 无法获取签名信息,请上传有效包(110506)
  12. profile 安卓work_androidWorkProfileGeneralDeviceConfiguration 资源类型
  13. 即时通讯软件开发界公认的说法
  14. python所有软件-python
  15. 脚本重启项目-定时启动
  16. 程序员为维持游戏开发被迫炒股,竟变成千万富翁
  17. window.crypto.subtle进行rsa-oaep加密
  18. 如何优雅的在word打公式
  19. 通信管线及宽带接入工程建设中主要涉及的 设计、施工及验收规范
  20. Android 7.1开机之后APN的加载及拨号上网流程分析

热门文章

  1. 下拉列表(select标签)
  2. 魔方APP项目-04-用户模块API接口、Marshmallow,基本构造器(Schema),Schema数据序列化、Schema数据反序列化、反序列化对数据验证、模型构造器(ModelSchema)
  3. freemarker遍历list处理第一个、最后一个元素
  4. Revit模型在Web端展示的免费方案
  5. 数据库的内外链接和左右链接
  6. echarts折线图曲线,每个值上面添加小圆点或者小圆圈
  7. 华为云数据库实验-openGauss金融场景化实验出现的问题
  8. c++ sin\cos函数引用
  9. 一个http请求的详细过程
  10. 理解事务四大特性(Transaction)——原子性、一致性、隔离性和持久性(ACID)