websocket基于socket,区别是需要握手,并将协议提升为websocket

一、socket_create方式

server端

<?php
error_reporting(E_ALL ^ E_NOTICE);/* Allow the script to hang around waiting for connections. */
set_time_limit(0);/* Turn on implicit output flushing so we see what we're getting* as it comes in. */
ob_implicit_flush();// 直接输出到浏览器$address = '192.168.80.3';
const MAX_LISTEN_NUM = 200;
$port = 8012;
$socketPool = [];
if (($hostSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
}// 设置TIME_WAIT状态socket可以复用
socket_set_option($hostSocket, SOL_SOCKET, SO_REUSEADDR,1); // 强制关闭socket开关:l_linger=0开启,1平滑关闭
$linger = array ('l_linger' => 0, 'l_onoff' => 1);
socket_set_option($hostSocket, SOL_SOCKET, SO_LINGER, $linger);if (socket_bind($hostSocket, $address, $port) === false) {echo "socket_bind() failed: reason: " . socket_strerror(socket_last_error($hostSocket)) . "\n";
}if (socket_listen($hostSocket, 200) === false) {echo "socket_listen() failed: reason: " . socket_strerror(socket_last_error($hostSocket)) . "\n";
}// 把所有socket存储在$sockets$socketPool[] = ['source'=>$hostSocket];try {while(true){$sockets = array_column($socketPool,'source'); // 拷贝clients$write = null; // 可读socket$except = null; // 异常socket$readNum = socket_select($sockets,$write,$except,null);foreach ($sockets as $socket) {if ($socket == $hostSocket) {// 处理连接情况$client = socket_accept($hostSocket);echo 'client:'.$client;echo 'client:'.(int)$client;if ($client < 0) {echo "socket_accept() failed: reason: " . socket_strerror(socket_last_error($hostSocket)) . "\n";} else {$socketPool[(int)$client] = ['source'=>$client,'handshake'=>false];}} else {$len = socket_recv($socket, $buff, 2048,0);echo 'len:'.$len;echo 'buff:'.$buff;$recvMsg = decode($buff);echo 'recvMsg:'.$recvMsg;// buff=null 证明socket已经关闭if ($buff == null) {socket_close($socket);unset($socketPool[(int)$socket]);break;}else if ($socketPool[(int)$socket]['handshake']) {// 发送消息 $answMsg = frame(json_encode(['code'=>0,'msg'=>$recvMsg,'data'=>[]]));$host = '';$port = '';// socket_getpeername($socket,$host,$port);// echo 'host:'.$host;// echo 'port:'.$port;@socket_write($socket,$answMsg,strlen($answMsg));} else {// 握手$socketPool[(int)$socket]['handshake'] =  true;handshake($socket,$buff,$address,$port);}}}}
} catch (Exception $e) {echo $e->getMessage();socket_shutdown($hostSocket);socket_close($hostSocket);
}function handshake($socket,$buffer,$host, $port)
{$headers = array();$lines = preg_split("/\r\n/", $buffer);foreach($lines as $line){$line = rtrim($line);if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)){$headers[$matches[1]] = $matches[2];}}$secKey = $headers['Sec-WebSocket-Key'];$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));//hand shaking header$upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" ."Upgrade: websocket\r\n" ."Connection: Upgrade\r\n" ."WebSocket-Origin: $host\r\n" ."WebSocket-Location: ws://$host:$port\r\n"."Sec-WebSocket-Version: 13\r\n" ."Sec-WebSocket-Accept:$secAccept\r\n\r\n";socket_write($socket, $upgrade);
}// 解析数据帧
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 frame($s) {$a = str_split($s, 125);if (count($a) == 1) {return "\x81" . chr(strlen($a[0])) . $a[0];}$ns = "";foreach ($a as $o) {$ns .= "\x81" . chr(strlen($o)) . $o;}return $ns;
}

web端

发送

// 结果

二、通过 stream_socket_server方式

server端

<?php$host = '192.168.80.3';
$port = 8012;
// $path = 'C:/Certbot/live/php.net/';
$transport = 'tcp';
// $ssl = ['ssl' => [// 'local_cert'  => $path . 'cert.pem',       // SSL Certificate// 'local_pk'    => $path . 'privkey.pem',    // SSL Keyfile// 'disable_compression' => true,             // TLS compression attack vulnerability// 'verify_peer'         => false,            // Set this to true if acting as an SSL client// 'ssltransport' => $transport,              // Transport Methods such as 'tlsv1.1', tlsv1.2'// ] ];
// $ssl_context = stream_context_create($ssl);
$server = stream_socket_server($transport . '://' . $host . ':' . $port, $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN);
if (!$server) {  die("$errstr ($errno)"); }
$socketPool[] = $server;// 保存所有连接
$write  = NULL;
$except = NULL;
const MAX_LISTEN_NUM = 200;// 最大链接数try {while (true) {$curSockets = $socketPool; // 备份stream_select($curSockets, $write, $except, MAX_LISTEN_NUM);// 获取可读socketforeach ($curSockets as $socket) {if ($server == $socket) { // 请求连接$client = @stream_socket_accept($server);if ($client == false) {continue;} else {$socketPool[(int)$client] = $client;$ip = stream_socket_get_name( $client, true );echo "New Client connected from $ip\n";// 使用阻塞发送握手stream_set_blocking($client,true);$headers = fread($client, 1024);handshake($client, $headers, $host, $port);   stream_set_blocking($client,false);// 握手后发送连接成功消息fwrite($client, mask('connected ok'));$key = array_search($server,$curSockets);unset($curSockets[$key]);        }} else {$buffer = stream_get_contents($socket);echo 'buffer:'.$buffer;  if ($buffer == false) { // 连接已关闭stream_socket_shutdown($server,STREAM_SHUT_WR);// 关闭读写fclose($socket);// 关闭连接unset($socketPool[(int)$socket]);} else {$recvMsg = unmask($buffer);$response = mask($recvMsg);fwrite($socket, $response);}}}}
} catch (Exception $e) {stream_socket_shutdown($server,STREAM_SHUT_WR);fclose($server);
}/*** 将socket接收到的乱码字符转为UTF-8字符* @param  string $str* @return string*/function unmask($text) {$length = @ord($text[1]) & 127;echo 'text'.$text.PHP_EOL;echo 'text1'.$text[1].PHP_EOL;echo 'text-ord'.@ord($text[1]).PHP_EOL;echo 'length'.$length.PHP_EOL;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 = "";for ($i = 0; $i < strlen($data); ++$i) { $text .= $data[$i] ^ $masks[$i % 4];    }echo 'mask'.$text.PHP_EOL;return $text;
}
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('CCN', $b1, 127, $length);return $header.$text;
}function handshake($client, $rcvd, $host, $port){$headers = array();$lines = preg_split("/\r\n/", $rcvd);foreach($lines as $line){$line = rtrim($line);if(preg_match('/\A(\S+): (.*)\z/', $line, $matches)){$headers[$matches[1]] = $matches[2];}}$secKey = $headers['Sec-WebSocket-Key'];$secAccept = base64_encode(pack('H*', sha1($secKey . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));//hand shaking header$upgrade  = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n" ."Upgrade: websocket\r\n" ."Connection: Upgrade\r\n" ."WebSocket-Origin: $host\r\n" ."WebSocket-Location: wss://$host:$port\r\n"."Sec-WebSocket-Version: 13\r\n" ."Sec-WebSocket-Accept:$secAccept\r\n\r\n";fwrite($client, $upgrade);
}

web代码

<input type="text" name="msg" value="这是发送消息" id="msg"/><br>
<button onclick="send()">发送</button><br>
<button onclick="closeWs()">关闭连接</button>
<script>
var ws = new WebSocket('ws://192.168.80.3:8012/');ws.onopen = function(e){console.log("Connection open ...");ws.send("发送消息");
}
ws.onmessage = function(e){console.log("onmessage",e.data);
}ws.onclose = function(e){ws = null;
}function closeWs(){console.log('close ws');ws.close();
}
function send(){console.log('send');var msg = document.getElementById('msg').value;console.log('msg',msg);ws.send(msg);
}</script>

// 结果

关于pack参考我的另一篇文章:php pack/unpack学习
总结:
socket_create 和 stream_socket_server操作基本一致,只不过后者是文件流操作,前者是文件资源操作,没有本质区别

参考文章:
https://www.cnblogs.com/hustskyking/p/websocket-with-php.html
https://www.cnblogs.com/zhenbianshu/p/6111257.html

php socket之websocket相关推荐

  1. Socket 与 WebSocket

    本文原创版权归 zrong 所有,转载请于篇头位置标明原创作者及出处,以示尊重! 本文标题:Socket 与 WebSocket 本文链接:http://zengrong.net/post/2199. ...

  2. HTTP,TCP,UDP,Socket,WebSocket

    七层模型 五层模型 四层模型 应用层     表示层 应用层 应用层 会话层     传输层 传输层 传输层 网络层 网络层 网络层 数据链路层 数据链路层 链接层/实体层 物理层 物理层   二.这 ...

  3. Socket和Websocket

    Socket和Websocket Socket Socket当初设计的目的就是为了统一同一台计算机中进程之间的通信以及不同计算机进程之间的通信所设计的一个统一的接口.套接字最早是UC Berkeley ...

  4. TCP UDP socket http webSocket 之间的关系

    阅读目录 OSI & TCP/IP 模型 几者之间的关系 HTTP Socket WebSocket webSocket 概念 webSocket 优点 Websocket 的作用 ajax轮 ...

  5. Java Socket实现WebSocket服务器

    先说WebSocket的客户端,用Javascript写就行 var websocekt=new WebSocket("ws://127.0.0.0:8888");//8888是端 ...

  6. 使用 Nginx 代理 Socket.io/WebSocket 及 负载均衡配置

    2019独角兽企业重金招聘Python工程师标准>>> 转自:http://naux.me/using-nginx-with-socketio/ 0,介绍 Socket.io 一套是 ...

  7. Http、Socket、WebSocket之间联系与区别

    一.WebSocket和Socket区别 可以把WebSocket想象成HTTP(应用层),HTTP和Socket什么关系,WebSocket和Socket就是什么关系.HTTP 协议有一个缺陷:通信 ...

  8. Android端发送字符到Wed端,Android面试-socket和websocket

    Android与服务器的通信方式主要有两种,一是Http通信,一是Socket通信.两者的最大差异在于,http连接使用的是"请求-响应方式",即在请求时建立连接通道,当客户端向服 ...

  9. linux命令 socket,如何从linux中的命令行向socket.io websocket发送消息?

    是否可以使用linux中的命令行向我的localhost服务器(节点)发送socket.io消息?我不确定这是否可行--从稀缺的谷歌搜索结果来看,我猜这不可能或不复杂-- 我的socket.io代码如 ...

  10. 基于TCP Socket和Websocket实现的相互即时通信系统

    目录 摘 要 III ABSTRACT IV 第一章 引言 1 1.1 即时通信系统基本概念 1 1.2 即时通信系统的发展历程 1 1.3 系统研究目的和意义 1 1.4 系统可行性分析 2 第二章 ...

最新文章

  1. parfile解决exp时tables过多问题
  2. Maven 搭建多模块企业级项目
  3. python三角函数拟合_使用python进行数据拟合最小化函数
  4. 用深度强化学习玩atari游戏_被追捧为“圣杯”的深度强化学习已走进死胡同
  5. 数据科学基础_学习数据科学基础
  6. Python 一键转 Java?“Google 翻译”你别闹
  7. POST 请求出现异常!java.io.IOException: Server returned HTTP response code: 400 for URL
  8. Mac下开发ASP.NET Core应用,我用FineUICore!
  9. Project 3 :Python爬虫源码实现抓取1000条西刺免费代理IP-HTTPS并保存读取
  10. Unity3D AABB包围盒效果
  11. python二级题库刷题训练
  12. 中国矿业大学考研计算机应用技术,2020中国矿业大学计算机专业课考试科目变动...
  13. 2020年Android GMS 认证 boot logo 最新要求
  14. VS 注释多行与取消多行注释快捷键
  15. Quartus安装及使用教程(13版附安装包)
  16. [数据统计]百度在调低索引库的容量
  17. C语言读取磁盘分区信息(MBR、DPT、EBR)
  18. Google AIY 套件将亮相香港!人工智能新一轮风潮袭来!
  19. 人人都懂的RocketMQ基本原理
  20. Graphics2D.drawString中文乱码

热门文章

  1. swift 命名空间
  2. 升职加薪轮不到我,原来因为.....
  3. php $mail = new phpmailer();,PHP 利用QQ郵箱發送郵件「PHPMailer」
  4. 云计算包括哪几个层次的服务?云计算的三大服务层次
  5. Unbroken(坚不可摧)——Mateusz M
  6. Matplotlib中annotate详解
  7. python annotate函数_Matplotlib库 标注点函数annotate()
  8. LNMP环境搭建总结
  9. Middleware 中间件
  10. 入门Linux运维工程师,必须要掌握的10个技术点