php使用WebSocket详细教程之建立连接(一)
本次教程需要理解的内容:
- 什么是WebSocket?
- WebSocket可以用来干什么?
- 什么是WebSocket握手?
- php使用WebSocket的流程?
- php中WebSocket相关函数的作用?
(一)什么是WebSocket?
WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
(二)WebSocket的作用?
WebSock其实在平常使用,我们是时常见到的,用于实时通讯,例如我们常用的实时聊天、服务端向客户端消息推送、也可以实现踢用户下线功能。实时弹幕功能等等。
(三)什么是握手?
为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。
这是比较正式的理解,在接下来使用方式中会在介绍到握手的实际含义。
(四)php使用WebSocket的流程及相关函数的意义
这里代码注释都会进行逐一解释,所以就直接上代码,有什么不懂欢迎提出来。
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 | <?php //设置应该报告何种 PHP 错误 error_reporting (E_ALL^E_NOTICE); //设置脚本最大执行时间,0则为不限制 set_time_limit(0); //打开或关闭绝对(隐式)刷送 ob_implicit_flush(); //设置创建socket服务器的ip $address = "127.0.0.1" ; //设置socket监听的端口 $port =10000; //socket的resource,即前期初始化socket时返回的socket资源 $master ; //用来存储连接进来的用户信息的数组 $users ; //socket的连接池,即client连接进来的socket标志,一个数组 $sockets ; /** * 以下socket_?()方法都为创建一个socket必须的,且顺序不能乱,缺一不可 */ //创建一个socket $master =socket_create(AF_INET,SOCK_STREAM,SOL_TCP); //设置socket选项,1表示接受所有的数据包 socket_set_option( $master ,SOL_SOCKET,SO_REUSEADDR,1); //绑定socket到指定ip与端口 socket_bind( $master , $address , $port ); //监听已连接的socket socket_listen( $master ); //初始化sockets连接池 $sockets = array ( $master ); //对一些必要信息的输出记录 echo "socket已连接,时间:" . date ( "Y-m-d H:i:s" ). "\n" ; echo "监听中:" . $address . ":" . $port . "\n" ; //设置循环使脚本持续运行处理消息 while (true){ //用来检测是否有变化的数组(就是有新消息到或者有客户端连接/断开) $changes = $sockets ; $write =NULL; $except =NULL; /** * 对于个人理解,这个函数的作用为阻塞程序往下执行,它会不停的检验$changes是否有变化,没有变化就将阻断程序往下执行。 * 只有出现$changes出现变化(有新消息到或者有客户端连接/断开)才会对继续执行程序。 * 很重要的一个函数。有的说法是同时接受多个连接的关键 * @param array $write是监听是否有客户端写数据,传入NULL是不关心是否有写变化。 * @param array $except是$sockets里面要被排除的元素,传入NULL是”监听”全部。 * @param int 最后一个参数是超时时间 * 如果为0:则立即结束 * 如果为n>1: 则最多在n秒后结束,如遇某一个连接有新动态,则提前返回 * 如果为null:如遇某一个连接有新动态,则返回 */ socket_select( $changes , $write , $except ,NULL); //这里遍历检测出出现何种变化,然后进行处理 foreach ( $changes as $sock ){ //当下列条件的满足时,表示有新用户连接进来 if ( $sock == $master ){ //接受该用户的连接 $client =socket_accept( $master ); //给这个用户生成一个独一无二的id,用与获取该用户的信息的唯一标识。 $key =uniqid(); //将新用户存入socke连接池 $sockets []= $client ; //记录用户连接的信息,为了方便能对指定用户发送消息。其中handshake代表服务器与客户端握手与否,socket的另外一个重要的操作 $users [ $key ]= array ( "socket" => $client , "handshake" =>false, ); echo "分配id为" . $key . "的用户连接\n" ; } // 剩下的为用户断开连接或者用户向服务端发送信息 else { $len =0; //收到数据的长度 $buffer = '' ; //收到的数据 /** * socket_recv( resource $socket, string &$buf, int $len, int $flags) : int * 函数 socket_recv() 从 socket 中接受长度为 最大为$len 字节的数据,并保存在 buf 中,$l返回的为实际读取数据的长度。 * socket_recv() 用于从已连接的socket中接收数据。除此之外,可以设定一个或多个 flags 来控制函数的具体行为。 */ //通过循环的方式读取全部数据$len可根据自身设置 do { $l =socket_recv( $sock , $buf ,1000,0); $len += $l ; $buffer .= $buf ; } while ( $l ==1000); $tmpk ; //获取操作用户的key,即一开始分配的唯一标识id foreach ( $users as $k => $v ){ //$k为键,$v为值 if ( $sock == $v [ 'socket' ]){ //获取连接的用户数组users,当users里存在有只返回该用户被分配的唯一id $tmpk = $k ; } } // 如果数据长度小于7为断开连接 if ( $len <7){ socket_close( $users [ $k ][ 'socket' ]); //关闭该用户连接,可以写成socket_close($sock),这种写法是封装后的写法,为了容易看懂不进行封装; unset( $users [ $tmpk ]); //销毁指定的users的某个用户信息 $sockets = array ( $master ); //可以理解为初始化sockets连接池 //遍历users数组,将连接的信息存入$sockets中 foreach ( $users as $v ){ $sockets []= $v [ 'socket' ]; } echo "id为" . $tmpk . "用户断开连接\n" ; continue ; } //服务端与用户握手 //如果没有与客户端握手,数据交换都会错误。 //一旦服务器发送了以下头文件,握手就完成了,我们就可以交换数据了,可以理解为检验身份差不多的意思 if (! $users [ $tmpk ][ 'handshake' ]){ //截取客户端请求时发送给服务端Sec-WebSocket-Key的值并加密,其中$key后面的一部分258EAFA5-E914-47DA-95CA-C5AB0DC85B11字符串应该是固定的 $buf = substr ( $buffer , strpos ( $buffer , 'Sec-WebSocket-Key:' )+18); $key =trim( substr ( $buf ,0, strpos ( $buf , "\r\n" ))); //前两步可以直接替换为trim(substr($buffer,strpos($buffer,'Sec-WebSocket-Key:')+16)) $new_key = base64_encode (sha1( $key . "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ,true)); //向客户端返回该信息,也就是所说的握手。 $hand_message = "HTTP/1.1 101 Switching Protocols\r\n" . "Upgrade: websocket\r\n" . "Sec-Websocket-Version: 13\r\n" . "Connection: Upgrade\r\n" . "Sec-Websocket-Accept: " . $new_key . "\r\n\r\n" ; /** * writes to the socket from the given buffer * 向指定的socket发送信息 * 这里向用户发送握手信息 */ $status =socket_write( $users [ $tmpk ][ 'socket' ], $hand_message , strlen ( $hand_message )); if ( $status ){ echo "与用户id" . $tmpk . "握手成功\n" ; echo $hand_message . "\n" ; } } // 最后剩下的就为用户发送消息,做接收操作,由于需要包含二进制数据的转换,需了解websocket的数据收发协议,下一篇将更新接下来数据的处理 else { //接收数据处理操作 } } } } ?> |
结语:由于接下来数据的接收与发送,会涉及到数据的解码与编码,下一篇内容将会介绍数据的发送与接收,对各个操作都详细的解释。
自己学习过程中没看到叫详细的教程,就写个专题关于WebSocket的使用,当然也可以使用workman等开源通讯框架,少去很多麻烦,在这里也是为了构造自己的通讯方式,自己编写。
————————————————
原文链接:https://blog.csdn.net/Vae_sun/article/details/90318326
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具