php socket:websocket
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);// 获取可读socket
foreach ($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
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgekjja
系列文章
更多
同类精品
更多
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
怎样阻止微信小程序自动打开
PHP中文网 06-13 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01