1 <?php2 /**3 * author: NickBai4 * createTime: 2016/12/9 0009 下午 4:175 */
6 namespace NickBai;7
8 classSocketChat9 {10 private $timeout = 60; //超时时间
11 private $handShake = False; //默认未牵手
12 private $master = 1; //主进程
13 private $port = 2000; //监听端口
14 private static $connectPool = []; //连接池
15 private static $maxConnectNum = 1024; //最大连接数
16 private static $chatUser = []; //参与聊天的用户
17
18
19 public function __construct( $port = 0)20 {21 !empty( $port ) && $this->port = $port;22 $this->startServer();23 }24
25 //开始服务器
26 public functionstartServer()27 {28 $this->master = socket_create_listen( $this->port );29 if( !$this->master ) throw new Exception('listen $this->port fail !');30
31 $this->runLog("Server Started : ".date('Y-m-d H:i:s'));32 $this->runLog("Listening on : 127.0.0.1 port " . $this->port);33 $this->runLog("Master socket : ".$this->master."n");34
35 self::$connectPool[] = $this->master;36
37 while( true){38 $readFds = self::$connectPool;39 //阻塞接收客户端链接
40 @socket_select( $readFds, $writeFds, $e = null, $this->timeout );41
42 foreach( $readFds as $socket){43 //当前链接 是主进程
44 if( $this->master == $socket){45
46 $client = socket_accept( $this->master ); //接收新的链接
47 $this->handShake = False;48
49 if ($client < 0){50 $this->log('clinet connect false!');51 continue;52 } else{53 //超过最大连接数
54 if( count( self::$connectPool ) > self::$maxConnectNum)55 continue;56
57 //加入连接池
58 $this->connect( $client);59 }60
61 }else{62 //不是主进程,开始接收数据
63 $bytes = @socket_recv($socket, $buffer, 2048, 0);64 //未读取到数据
65 if( $bytes == 0){66 $this->disConnect( $socket);67 }else{68 //未握手 先握手
69 if( !$this->handShake ){70
71 $this->doHandShake( $socket, $buffer);72 }else{73
74 //如果是已经握完手的数据,广播其发送的消息
75 $buffer = $this->decode( $buffer);76 $this->parseMessage( $buffer, $socket);77 }78 }79
80 }81 }82
83 }84 }85
86 //解析发送的数据
87 public function parseMessage( $message, $socket)88 {89 //msg type 1 初始化 2 通知 3 一般聊天 4 断开链接 5 获取在线用户 6 通知下线
90 $message = json_decode( $message, true);91 switch( $message['type'] ){92
93 case 1:
94 $this->bind( $socket, $message);95 //通知其他客户端,当前用户上线
96 $msg =[97 'type' => "2",
98 'msg' => 'online',
99 'avar' => $message['avar']100 ];101 $this->sendToAll( $socket, $msg);102 //更新在线用户
103 $this->freshOnlineUser();104
105 break;106 case 3:
107 $this->sendToAll( $socket, $message);108 break;109 case 4:
110 //通知用户离线
111 $msgOutline =[112 'type' => '6',
113 'user' => self::$chatUser[(int)$socket]['user']114 ];115 $this->tellOnlineInfo( $msgOutline);116 //断开 要离线的用户
117 $this->disConnect( $socket);118 //更新在线用户
119 $this->freshOnlineUser();120
121 break;122 default:
123 break;124 }125 }126
127 //用户--链接 绑定
128 public function bind( $socket, $user)129 {130 self::$chatUser[(int) $socket] =[131 'user' => $user['user'],
132 'avar' => $user['avar']133 ];134 }135
136 //用户--链接 解绑
137 public function unBind( $socket)138 {139 unset( self::$chatUser[(int) $socket] );140 }141
142 //获取在线用户
143 public functiongetOnlineUser()144 {145 return self::$chatUser;146 }147
148 //更新在线用户
149 public functionfreshOnlineUser()150 {151 $msgOnlie =[152 'type' => "5",
153 'msg' => 'online user',
154 'info' => self::$chatUser
155 ];156 $this->tellOnlineInfo( $msgOnlie);157 }158
159 //广播所有的客户端(排除自己和master)
160 public function sendToAll( $client, $mess)161 {162 //拼装发送者的名称
163 $mess['user'] = self::$chatUser[(int) $client]['user'];164 $mess['stime'] = date('Y-m-d H:i:s');165
166 foreach( self::$connectPool as $socket){167 if( $socket != $this->master && $socket != $client){168 $this->send( $socket, $mess);169 }170 }171 }172
173 //广播客户端在线用户信息
174 public function tellOnlineInfo( $mess)175 {176 foreach( self::$connectPool as $socket){177 if( $socket != $this->master ){178 $this->send( $socket, $mess);179 }180 }181 }182
183 //处理发送信息
184 public function send( $client, $msg)185 {186 $msg = $this->frame( json_encode( $msg) );187 socket_write( $client, $msg, strlen($msg) );188 }189
190 //握手协议
191 function doHandShake($socket, $buffer)192 {193 list($resource, $host, $origin, $key) = $this->getHeaders($buffer);194 $upgrade = "HTTP/1.1 101 Switching Protocolrn" .
195 "Upgrade: websocketrn" .
196 "Connection: Upgradern" .
197 "Sec-WebSocket-Accept: " . $this->calcKey($key) . "rnrn"; //必须以两个回车结尾
198
199 socket_write($socket, $upgrade, strlen($upgrade));200 $this->handShake = true;201 return true;202 }203
204 //获取请求头
205 function getHeaders( $req)206 {207 $r = $h = $o = $key = null;208 if (preg_match("/GET (.*) HTTP/" , $req, $match)) { $r = $match[1]; }209 if (preg_match("/Host: (.*)rn/" , $req, $match)) { $h = $match[1]; }210 if (preg_match("/Origin: (.*)rn/" , $req, $match)) { $o = $match[1]; }211 if (preg_match("/Sec-WebSocket-Key: (.*)rn/", $req, $match)) { $key = $match[1]; }212 return [$r, $h, $o, $key];213 }214
215 //验证socket
216 function calcKey( $key)217 {218 //基于websocket version 13
219 $accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));220 return $accept;221 }222
223
224 //打包函数 返回帧处理
225 public function frame( $buffer)226 {227 $len = strlen($buffer);228 if ($len <= 125) {229
230 return "x81" . chr($len) . $buffer;231 } else if ($len <= 65535) {232
233 return "x81" . chr(126) . pack("n", $len) . $buffer;234 } else{235
236 return "x81" . char(127) . pack("xxxxN", $len) . $buffer;237 }238 }239
240 //解码 解析数据帧
241 function decode( $buffer)242 {243 $len = $masks = $data = $decoded = null;244 $len = ord($buffer[1]) & 127;245
246 if ($len === 126) {247 $masks = substr($buffer, 4, 4);248 $data = substr($buffer, 8);249 }250 else if ($len === 127) {251 $masks = substr($buffer, 10, 4);252 $data = substr($buffer, 14);253 }254 else{255 $masks = substr($buffer, 2, 4);256 $data = substr($buffer, 6);257 }258 for ($index = 0; $index < strlen($data); $index++) {259 $decoded .= $data[$index] ^ $masks[$index % 4];260 }261 return $decoded;262 }263
264 //客户端链接处理函数
265 function connect( $socket)266 {267 array_push( self::$connectPool, $socket);268 $this->runLog("n" . $socket . " CONNECTED!");269 $this->runLog(date("Y-n-d H:i:s"));270 }271
272 //客户端断开链接函数
273 function disConnect( $socket)274 {275 $index = array_search( $socket, self::$connectPool);276 socket_close( $socket);277
278 $this->unBind( $socket);279 $this->runLog( $socket . " DISCONNECTED!");280 if ($index >= 0){281 array_splice( self::$connectPool, $index, 1);282 }283 }284
285 //打印运行信息
286 public function runLog( $mess = '')287 {288 echo $mess . PHP_EOL;289 }290
291 //系统日志
292 public function log( $mess = '')293 {294 @file_put_contents( './' . date("Y-m-d") . ".log", date('Y-m-d H:i:s') . " " . $mess . PHP_EOL,FILE_APPEND );295 }296 }
本文发布于:2024-01-27 19:03:53,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17063534482051.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |