websocket+php socket实现聊天室
原文地址:http://www.cnblogs.com/nickbai/articles/6169745.html
这两天用了点时间,研究了一下,用php socket+ websocket实现了一个小型的聊天室。我采用的是 select/poll 的同步模型,虽然扛不住很大的并发,但是理论上 维持 几百人在线还是可以的。
目前完成了第一版。这一版的由于采用的是 select/poll 和单进程,所以在win下面就可以运行。不需要额外的其他扩展支持。
我最近在 看云 发表了 ThinkPHP5+workerman+layIM打造聊天系统 教程,感兴趣的可以去看看。传送门:ThinkPHP5+workerman+layIM打造聊天系统
ichat v3.0 版本正在和 layim 合作中,详情可以参看 layim.layui.com。现开通了 ichat 线上预览功能。地址 :ichat v3预览
项目的依赖:
php版本大于 5.4,浏览器支持 websocket和localstorage。
看一下核心的服务端代码吧:
1 <?php2 /**3 * author: NickBai4 * createTime: 2016/12/9 0009 下午 4:175 */6 namespace NickBai;7 8 class SocketChat9 {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 function startServer()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 function getOnlineUser() 144 { 145 return self::$chatUser; 146 } 147 148 //更新在线用户 149 public function freshOnlineUser() 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 Protocol\r\n" . 195 "Upgrade: websocket\r\n" . 196 "Connection: Upgrade\r\n" . 197 "Sec-WebSocket-Accept: " . $this->calcKey($key) . "\r\n\r\n"; //必须以两个回车结尾 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: (.*)\r\n/" , $req, $match)) { $h = $match[1]; } 210 if (preg_match("/Origin: (.*)\r\n/" , $req, $match)) { $o = $match[1]; } 211 if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $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 }
客户端的代码,篇幅有限,我就不放出了。项目已经放入 本人github,需要了解的请 关注 :https://github.com/nick-bai/HappyChat
看一下页面效果吧:
ab并发测试:
参考文章:
http://blog.csdn.net/shagoo/article/details/6396089
http://www.cnblogs.com/hustskyking/p/websocket-with-php.html
https://www.web-tinker.com/article/20306.html
声明:本文内容仅是本人学习的记录,不保证在项目中可用,若引用此代码导致了严重后果,本人不承担任何法律责任。
websocket+php socket实现聊天室相关推荐
- rudesocket如何使用_[WebSocket入门]手把手搭建WebSocket多人在线聊天室(SpringBoot+WebS...
前言 本文中搭建了一个简易的多人聊天室,使用了WebSocket的基础特性. 源代码来自老外的一篇好文: 本文内容摘要: 初步理解WebSocket的前后端交互逻辑 手把手使用 SpringBoot ...
- 教你从零开始用WebSocket打造一个IM聊天室
之前我们在 IM即时聊天室(一):WebSocket 和 IM即时聊天室(二): Socket.io + Node.js 两篇文章中介绍了搭建一个IM的所需的技术栈和通信原理.那在这篇文章里我们就来详 ...
- SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室
SpringBoot入门建站全系列(二十七)WebSocket做简单的聊天室 一.概述 WebSocket 是一种网络通信协议.RFC6455 定义了它的通信标准. WebSocket 是 HTML5 ...
- Nodejs+webSocket搭建多人聊天室
NodeJs+webSocket搭建多人聊天室 准备的东西: 第一步:安装插件并且完善服务端 第二步 :搭建客户端并与服务端的通信 第三步 :添加CSS样式 第四步:总结 今天花了一个上午的时间去学习 ...
- 使用Socket模拟聊天室
使用Socket模拟聊天室 文章目录 使用Socket模拟聊天室 客户端 客户端读线程 客户端写线程 客户主函数 服务端 服务端线程 服务端主函数 客户端 客户端读线程 public class Ec ...
- Socket.IO聊天室~简单实用
小编心语:大家过完圣诞准备迎元旦吧~小编在这里预祝大家元旦快乐!!这一次要分享的东西小编也不是很懂啊,总之小编把它拿出来是觉地比较稀奇,而且程序也没有那么难,是一个比较简单的程序,大家可以多多试试~ ...
- 使用Node+websocket实现简易1v1聊天室(前端+服务器)
使用Node+websocket实现简易1v1聊天室(前端+服务器) 前提: 安装好node环境~~~ 可使用 node -v 和 npm -v 查看是否装好 实现逻辑: 用户A 用户B 服务器 用户 ...
- Android 基于Socket的聊天室
原文地址为: Android 基于Socket的聊天室 Socket是TCP/IP协议上的一种通信,在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路.一旦建立了虚拟的网络链路, ...
- 【java毕业设计】基于java+原生Sevlet+socket的聊天室系统设计与实现(毕业论文+程序源码)——聊天室系统
基于java+原生Sevlet+socket的聊天室系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+原生Sevlet+socket的聊天室系统设计与实现,文章末尾附有本毕业设 ...
最新文章
- linux gcc 宏定义 __GNUC__ __GNUC_MINOR__ 版本区分
- KinectFusion——微软基于KINECT的实时三维重建项目(KINECT)
- golang字符串处理
- python五十一:动态导入模块,通过字符串导入模块
- UA OPTI501 电磁波 Lorentz Oscillator Model 1 Drude-Lorentz模型
- Leetcode 剑指 Offer 03. 数组中重复的数字 (每日一题 20210614)
- 【直播回放】150分钟详解模型压缩理论和实践
- (非)对称加密算法在https中的应用(加密过程以及CA颁发、验证)
- Mysql 电商常用的时间操作(当天,昨天,7天,30天,半年,全年,季度等等)...
- Python argv小结
- 风口来了第二期——电子科学与技术专业现状和前景介绍分享
- 大疆DJI2019届秋季招聘笔试:机器学习算法工程师
- AIDL 方法参数的in out inout前缀作用
- 吉林大学计算机学院杜天宇,2016年全国研究生数学建模竞赛获奖名单
- 【CCF】小中大(C++)
- 传世之文《Teach Yourself Programming in Ten Years》十年学会编程
- Oracle 18c新特性-Memoptimized Rowstore(内存优化的行存储)
- java 函数(方法)
- Java实验报告之java面向对象编程
- duck java_Duck类型和(java)接口概念