PHP - Swoole websocket理解
php swoole实现websocket功能
1.确保安装了swoole扩展。
2.撰写服务程序
<?php
//创建websocket服务器对象,监听0.0.0.0:9502端口
$ws = new swoole_websocket_server("0.0.0.0", 9502);
//监听WebSocket连接打开事件
$ws->on('open', function ($ws, $request) {
echo "connection open: {$request->fd}\n";
$ws->push($request->fd, "hello, welcome\n");
});
//监听WebSocket消息事件
$ws->on('message', function ($ws, $frame) {
echo "Message:".$frame->data."\n";
foreach($ws->connections as $key => $fd) {
$ws->push($fd, "{$frame->data}");
}
});
//监听WebSocket连接关闭事件
$ws->on('close', function ($ws, $fd) {
echo "client-{$fd} is closed\n";
});
$ws->start();
3.开启服务
[root@localhost swooleTest]# php ws_serv.php
4.查看服务是否开启
[root@localhost swooleTest]# netstat -anp | grep :9502
tcp 0 0 0.0.0.0:9502 0.0.0.0:* LISTEN 3502/php
查看进程情况
[root@localhost swooleTest]# ps -ef | grep 3502
root 3502 2903 0 21:09 pts/1 00:00:00 php ws_serv.php
root 3503 3502 0 21:09 pts/1 00:00:00 php ws_serv.php
这个时候需要客户端连接测试了。
客户端可以是PHP,也可以是JS中的客户端。
下面通过JS连接websocket:
<script>
var ws;
$(function(){
link();
send();
})
function link () {
ws = new WebSocket("ws://192.168.70.66:9502");//连接服务器
ws.onopen = function(event){
console.log(event);
alert('连接了');
};
ws.onmessage = function (event) {
alert(event.data);
}
ws.onclose = function(event){alert("已经与服务器断开连接\r\n当前连接状态:"+this.readyState);};
ws.onerror = function(event){alert("WebSocket异常!");};
}
function send() {
ws.send("我是jq,我连接了。");
}
</script>
当执行客户端连接和发送消息的时候,服务端能够监听到。
[root@localhost swooleTest]# php ws_serv.php
connection open: 1
Message:我是jq,我连接了。
当其他客户端,发送消息的时候,服务端都能监听到。然后向其他在线的客户端发送消息。
下面是php客户端连接的情况:
<?php
$cli = new swoole_http_client('127.0.0.1', 9502);
$cli->setHeaders(['Trace-Id' => md5(time()),]);
$cli->on('message', function ($_cli, $frame) {
var_dump($frame);
});
$cli->upgrade('/', function ($cli) {
echo $cli->body;
$cli->push("hello world");
});
[root@localhost swooleTest]# php async_client.php
object(Swoole\WebSocket\Frame)#3 (3) {
["finish"]=>
bool(true)
["opcode"]=>
int(1)
["data"]=>
string(11) "hello world"
}
服务端也监听到了。
[root@localhost swooleTest]# php ws_serv.php
connection open: 1
Message:我是jq,我连接了。
connection open: 2
Message:hello world
客户端同样也能监听到其他客户端的情况。
[root@localhost swooleTest]# php async_client.php
object(Swoole\WebSocket\Frame)#3 (3) {
["finish"]=>
bool(true)
["opcode"]=>
int(1)
["data"]=>
string(11) "hello world"
}
object(Swoole\WebSocket\Frame)#3 (3) {
["finish"]=>
bool(true)
["opcode"]=>
int(1)
["data"]=>
string(26) "我是jq,我连接了。"
}
同步WebSocketClient.php,sync_client.php
<?php
class WebSocketClient
{
const VERSION = '0.1.4';
const TOKEN_LENGHT = 16;
const TYPE_ID_WELCOME = 0;
const TYPE_ID_PREFIX = 1;
const TYPE_ID_CALL = 2;
const TYPE_ID_CALLRESULT = 3;
const TYPE_ID_ERROR = 4;
const TYPE_ID_SUBSCRIBE = 5;
const TYPE_ID_UNSUBSCRIBE = 6;
const TYPE_ID_PUBLISH = 7;
const TYPE_ID_EVENT = 8;
const OPCODE_CONTINUATION_FRAME = 0x0;
const OPCODE_TEXT_FRAME = 0x1;
const OPCODE_BINARY_FRAME = 0x2;
const OPCODE_CONNECTION_CLOSE = 0x8;
const OPCODE_PING = 0x9;
const OPCODE_PONG = 0xa;
const CLOSE_NORMAL = 1000;
const CLOSE_GOING_AWAY = 1001;
const CLOSE_PROTOCOL_ERROR = 1002;
const CLOSE_DATA_ERROR = 1003;
const CLOSE_STATUS_ERROR = 1005;
const CLOSE_ABNORMAL = 1006;
const CLOSE_MESSAGE_ERROR = 1007;
const CLOSE_POLICY_ERROR = 1008;
const CLOSE_MESSAGE_TOO_BIG = 1009;
const CLOSE_EXTENSION_MISSING = 1010;
const CLOSE_SERVER_ERROR = 1011;
const CLOSE_TLS = 1015;
private $key;
private $host;
private $port;
private $path;
/**
* @var swoole_client
*/
private $socket;
private $buffer = '';
private $origin = null;
/**
* @var bool
*/
private $connected = false;
public $returnData = false;
/**
* @param string $host
* @param int $port
* @param string $path
*/
function __construct($host = '127.0.0.1', $port = 8080, $path = '/', $origin = null)
{
$this->host = $host;
$this->port = $port;
$this->path = $path;
$this->origin = $origin;
$this->key = $this->generateToken(self::TOKEN_LENGHT);
}
/**
* Disconnect on destruct
*/
function __destruct()
{
$this->disconnect();
}
/**
* Connect client to server
*
* @return $this
*/
public function connect()
{
$this->socket = new \swoole_client(SWOOLE_SOCK_TCP);
if (!$this->socket->connect($this->host, $this->port))
{
return false;
}
$this->socket->send($this->createHeader());
return $this->recv();
}
public function getSocket()
{
return $this->socket;
}
/**
* Disconnect from server
*/
public function disconnect()
{
$this->connected = false;
$this->socket->close();
}
public function close($code = self::CLOSE_NORMAL, $reason = '')
{
$data = pack('n', $code) . $reason;
return $this->socket->send(swoole_websocket_server::pack($data, self::OPCODE_CONNECTION_CLOSE, true));
}
public function recv()
{
$data = $this->socket->recv();
if ($data === false)
{
echo "Error: {$this->socket->errMsg}";
return false;
}
$this->buffer .= $data;
$recv_data = $this->parseData($this->buffer);
if ($recv_data)
{
$this->buffer = '';
return $recv_data;
}
else
{
return false;
}
}
/**
* @param string $data
* @param string $type
* @param bool $masked
* @return bool
*/
public function send($data, $type = 'text', $masked = false)
{
switch($type)
{
case 'text':
$_type = WEBSOCKET_OPCODE_TEXT;
break;
case 'binary':
case 'bin':
$_type = WEBSOCKET_OPCODE_BINARY;
break;
case 'ping':
$_type = WEBSOCKET_OPCODE_PING;
break;
default:
return false;
}
return $this->socket->send(swoole_websocket_server::pack($data, $_type, true, $masked));
}
/**
* Parse received data
*
* @param $response
*/
private function parseData($response)
{
if (!$this->connected)
{
$response = $this->parseIncomingRaw($response);
if (isset($response['Sec-Websocket-Accept'])
&& base64_encode(pack('H*', sha1($this->key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'))) === $response['Sec-Websocket-Accept']
)
{
$this->connected = true;
return true;
}
else
{
throw new \Exception("error response key.");
}
}
$frame = swoole_websocket_server::unpack($response);
if ($frame)
{
return $this->returnData ? $frame->data : $frame;
}
else
{
throw new \Exception("swoole_websocket_server::unpack failed.");
}
}
/**
* Create header for websocket client
*
* @return string
*/
private function createHeader()
{
$host = $this->host;
if ($host === '127.0.0.1' || $host === '0.0.0.0')
{
$host = 'localhost';
}
return "GET {$this->path} HTTP/1.1" . "\r\n" .
"Origin: {$this->origin}" . "\r\n" .
"Host: {$host}:{$this->port}" . "\r\n" .
"Sec-WebSocket-Key: {$this->key}" . "\r\n" .
"User-Agent: PHPWebSocketClient/" . self::VERSION . "\r\n" .
"Upgrade: websocket" . "\r\n" .
"Connection: Upgrade" . "\r\n" .
"Sec-WebSocket-Protocol: wamp" . "\r\n" .
"Sec-WebSocket-Version: 13" . "\r\n" . "\r\n";
}
/**
* Parse raw incoming data
*
* @param $header
*
* @return array
*/
private function parseIncomingRaw($header)
{
$retval = array();
$content = "";
$fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
foreach ($fields as $field)
{
if (preg_match('/([^:]+): (.+)/m', $field, $match))
{
$match[1] = preg_replace_callback('/(?<=^|[\x09\x20\x2D])./',
function ($matches)
{
return strtoupper($matches[0]);
},
strtolower(trim($match[1])));
if (isset($retval[$match[1]]))
{
$retval[$match[1]] = array($retval[$match[1]], $match[2]);
}
else
{
$retval[$match[1]] = trim($match[2]);
}
}
else
{
if (preg_match('!HTTP/1\.\d (\d)* .!', $field))
{
$retval["status"] = $field;
}
else
{
$content .= $field . "\r\n";
}
}
}
$retval['content'] = $content;
return $retval;
}
/**
* Generate token
*
* @param int $length
*
* @return string
*/
private function generateToken($length)
{
$characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"§$%&/()=[]{}';
$useChars = array();
// select some random chars:
for ($i = 0; $i < $length; $i++)
{
$useChars[] = $characters[mt_rand(0, strlen($characters) - 1)];
}
// Add numbers
array_push($useChars, rand(0, 9), rand(0, 9), rand(0, 9));
shuffle($useChars);
$randomString = trim(implode('', $useChars));
$randomString = substr($randomString, 0, self::TOKEN_LENGHT);
return base64_encode($randomString);
}
/**
* Generate token
*
* @param int $length
*
* @return string
*/
public function generateAlphaNumToken($length)
{
$characters = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
srand((float)microtime() * 1000000);
$token = '';
do
{
shuffle($characters);
$token .= $characters[mt_rand(0, (count($characters) - 1))];
} while (strlen($token) < $length);
return $token;
}
}
<?php
$opt = getopt("c:n:k:");
print_r($opt);
if (empty($opt['n']))
{
echo "examples: php sync_client.php -n 10000" . PHP_EOL;
return;
}
$count = $opt['n'];
require __DIR__ . "/WebSocketClient.php";
$host = '127.0.0.1';
$prot = 9502;
$client = new WebSocketClient($host, $prot);
$data = $client->connect();
//echo $data;
$data = "data";
for ($i = 0; $i < $count; $i++)
{
$client->send("hello swoole, number:" . $i . " data:" . $data);
echo "send over!" . PHP_EOL;
}
echo PHP_EOL . "======" . PHP_EOL;
sleep(1);
echo 'finish' . PHP_EOL;
具体的案例可以参考:[案例]
PHP - Swoole websocket理解的更多相关文章
- php开发面试题---php 对swoole的理解
php开发面试题---php 对swoole的理解 一.总结 一句话总结: 以战养学,实例驱动 swoole是披着PHP外衣的C程序:其实就是c.java那些语言里面的高阶功能:比如 持久连接.异步通 ...
- Swoole WebSocket 的应用
目录 概述 代码 小结 概述 这是关于 Swoole 学习的第三篇文章:Swoole WebSocket 的应用. 第二篇:Swoole Task 的应用 第一篇:Swoole Timer 的应用 什 ...
- swoole webSocket 聊天室示例
swoole1.7.9增加了内置的WebSocket服务器支持,通过几行PHP代码就可以写出一个异步非阻塞多进程的WebSocket服务器. 基于swoole websocket的用户上下线通知,在线 ...
- swoole websocket服务推送
用过workerman, 两个字"好用",对于swoole最近有时间也研究研究 swoole的websocket 很好实现 如官网 https://wiki.swoole.com/ ...
- swoole+websocket+redis实现一对一聊天
如同web端的QQ和微信一样,这是一个web端的聊天程序. 环境:ubuntu + php + swoole扩展 + redis + mysql Redis 实现每个连接websocket的服务都唯一 ...
- PHP swoole websocket协议上机指南
这一上机实验的开发环境用的是jetbrain公司的phpstorm 上级步骤如下: 创建websocket服务端 <?php $server = ); function onopen($serv ...
- php+swoole+websocket
//创建websocket服务器对象,监听0.0.0.0:9502端口 $ws = new swoole_websocket_server("0.0.0.0", 9502); // ...
- swoole webSocket服务
socket.php <?php //创建websocket服务器对象,监听0.0.0.0:9502端口 $ws = ); //监听WebSocket连接打开事件 (刚打开的时候会给客户端发送 ...
- swoole WebSocket 消息推送
server.php <?php //连接本地的 Redis 服务 $redis = new Redis(); $redis->connect('127.0.0.1', 6379); $r ...
随机推荐
- docker之DockerSwarm的了解
这次一起了解下docker Swarm,什么是dockerSwarm. 什么是docker Swarm 产品背景 使用docker的流程,ssh到一台服务器,运行docker命令来运行本机的docke ...
- 关于pytest的一些问题
一. 测试模块内部使用fixture和测试模块调用外部公共的fixture 1. unittest框架下的测试用例模块 from selenium import webdriver import un ...
- ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)-mysql.sock丢失解决方案
我们的LAMP是搭建在UBUNTU 12.04 LTS上的. LAMP是通过编译的方式进来安装的. 在一次处理意外挂机时由于未知的原因在重启后发现无法连接数据库了, 在打开网站时出现如下的的提示: E ...
- MongoCola使用教程 2 - MongoDB的Replset 初始化和配置
前言 首先再次感谢博客园的各位朋友.正是你们的关注才让我有信心将这个工具开发下去. 这周同样也有热心网友对于MongoCola存在的问题给予了反馈. 这次工具更新到了版本1.20,强化的地方是增加了R ...
- Django框架(二)
一:Django项目创建步骤: 方式1:命令创建: 进入指定目录 C:\Users\bing>F: F:\>cd mysite F:\mysite>django-admin star ...
- JSP的指令
JSP 指令 JSP指令用来设置整个JSP页面相关的属性,如网页的编码方式和脚本语言. 语法格式如下: <%@ directive attribute="value" %&g ...
- 《锋利的jQuery》读书笔记(DOM+事件)
前阵子买了一批书,就从锋利的jQuery看起吧,书中一些太过常规以及没有强记必要性的操作就不记录了. 1.DOM加载后执行JS $(document).ready(function(){ //.... ...
- React Native探索(五)使用fetch进行网络请求
相关文章 React Native探索系列 前言 React Native可以使用多种方式来请求网络,比如fetch.XMLHttpRequest以及基于它们封装的框架,fetch可以说是替代XMLH ...
- H5 pattern
pattern:正则表达式验证 例如: <input pattern="1[3578]\d{9}"> 可以省略^和$ 必须和required配合使用,否则在用户没有输 ...
- Kali Linux:使用nmap扫描主机
nmap-Network Mapper,是著名的网络扫描和嗅探工具包.他同样支持Windows和OS X. 扫描开放端口和判断操作系统类型 先让我们ping一段地址范围,找到启动的主机: # nmap ...