-
Notifications
You must be signed in to change notification settings - Fork 3
/
websocket.class.php
executable file
·191 lines (184 loc) · 5.64 KB
/
websocket.class.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
<?php
/*
创建类websocket($config);
$config结构:
$config=array(
'address'=>'192.168.0.200',//绑定地址
'port'=>'8000',//绑定端口
'event'=>'WSevent',//回调函数的函数名
'log'=>true,//命令行显示记录
);
回调函数返回数据格式
function WSevent($type,$event)
$type字符串 事件类型有以下三种
in 客户端进入
out 客户端断开
msg 客户端消息到达
均为小写
$event 数组
$event['k']内置用户列表的userid;
$event['sign']客户标示
$event['msg']收到的消息 $type='msg'时才有该信息
方法:
run()运行
search(标示)遍历取得该标示的id
close(标示)断开连接
write(标示,信息)推送信息
idwrite(id,信息)推送信息
属性:
$users 客户列表
结构:
$users=array(
[用户id]=>array('socket'=>[标示],'hand'=[是否握手-布尔值]),
[用户id]=>arr.....
)
*/
class websocket{
public $log;
public $event;
public $signets;
public $users;
public $master;
public function __construct($config){
if (substr(php_sapi_name(), 0, 3) !== 'cli') {
die("请通过命令行模式运行!");
}
error_reporting(E_ALL);
set_time_limit(0);
ob_implicit_flush();
$this->event = $config['event'];
$this->log = $config['log'];
$this->master=$this->WebSocket($config['address'], $config['port']);
$this->sockets=array('s'=>$this->master);
}
function WebSocket($address,$port){
$server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($server, $address, $port);
socket_listen($server);
$this->log('开始监听: '.$address.' : '.$port);
return $server;
}
function run(){
while(true){
$changes=$this->sockets;
@socket_select($changes,$write=NULL,$except=NULL,NULL);
foreach($changes as $sign){
if($sign==$this->master){
$client=socket_accept($this->master);
$this->sockets[]=$client;
$user = array(
'socket'=>$client,
'hand'=>false,
);
$this->users[] = $user;
$k=$this->search($client);
$eventreturn = array('k'=>$k,'sign'=>$sign);
$this->eventoutput('in',$eventreturn);
}else{
$len=socket_recv($sign,$buffer,2048,0);
$k=$this->search($sign);
$user=$this->users[$k];
if($len<7){
$this->close($sign);
$eventreturn = array('k'=>$k,'sign'=>$sign);
$this->eventoutput('out',$eventreturn);
continue;
}
if(!$this->users[$k]['hand']){//没有握手进行握手
$this->handshake($k,$buffer);
}else{
$buffer = $this->uncode($buffer);
$eventreturn = array('k'=>$k,'sign'=>$sign,'msg'=>$buffer);
$this->eventoutput('msg',$eventreturn);
}
}
}
}
}
function search($sign){//通过标示遍历获取id
foreach ($this->users as $k=>$v){
if($sign==$v['socket'])
return $k;
}
return false;
}
function close($sign){//通过标示断开连接
$k=array_search($sign, $this->sockets);
socket_close($sign);
unset($this->sockets[$k]);
unset($this->users[$k]);
}
function handshake($k,$buffer){
$buf = substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+18);
$key = trim(substr($buf,0,strpos($buf,"\r\n")));
$new_key = base64_encode(sha1($key."258EAFA5-E914-47DA-95CA-C5AB0DC85B11",true));
$new_message = "HTTP/1.1 101 Switching Protocols\r\n";
$new_message .= "Upgrade: websocket\r\n";
$new_message .= "Sec-WebSocket-Version: 13\r\n";
$new_message .= "Connection: Upgrade\r\n";
$new_message .= "Sec-WebSocket-Accept: " . $new_key . "\r\n\r\n";
socket_write($this->users[$k]['socket'],$new_message,strlen($new_message));
$this->users[$k]['hand']=true;
return true;
}
function uncode($str){
$mask = array();
$data = '';
$msg = unpack('H*',$str);
$head = substr($msg[1],0,2);
if (hexdec($head{1}) === 8) {
$data = false;
}else if (hexdec($head{1}) === 1){
$mask[] = hexdec(substr($msg[1],4,2));
$mask[] = hexdec(substr($msg[1],6,2));
$mask[] = hexdec(substr($msg[1],8,2));
$mask[] = hexdec(substr($msg[1],10,2));
$s = 12;
$e = strlen($msg[1])-2;
$n = 0;
for ($i=$s; $i<= $e; $i+= 2) {
$data .= chr($mask[$n%4]^hexdec(substr($msg[1],$i,2)));
$n++;
}
}
return $data;
}
function code($msg){
$msg = preg_replace(array('/\r$/','/\n$/','/\r\n$/',), '', $msg);
$frame = array();
$frame[0] = '81';
$len = strlen($msg);
$frame[1] = $len<16?'0'.dechex($len):dechex($len);
$frame[2] = $this->ord_hex($msg);
$data = implode('',$frame);
return pack("H*", $data);
}
function ord_hex($data) {
$msg = '';
$l = strlen($data);
for ($i= 0; $i<$l; $i++) {
$msg .= dechex(ord($data{$i}));
}
return $msg;
}
function idwrite($id,$t){//通过id推送信息
if(!$this->users[$id]['socket']){return false;}//没有这个标示
$t=$this->code($t);
return socket_write($this->users[$id]['socket'],$t,strlen($t));
}
function write($k,$t){//通过标示推送信息
$t=$this->code($t);
return socket_write($k,$t,strlen($t));
}
function eventoutput($type,$event){//事件回调
call_user_func($this->event,$type,$event);
}
function log($t){//控制台输出
if($this->log){
$t=$t."\r\n";
// fwrite(STDOUT, iconv('utf-8','gbk//IGNORE',$t));
fwrite(STDOUT, $t);
}
}
}