PHP原生Socket搭建WebSocket服务握手失败客户端连接异常问题
WebSocket握手失败问题修复方案
你遇到的连接失败问题是由多个细节不符合WebSocket协议规范导致的,可按以下步骤逐一修复:
现存问题排查
- 响应头结束标识缺失:WebSocket握手属于HTTP升级协议,响应头所有字段结束后需要额外添加
\r\n,也就是响应头末尾必须是两个连续的换行符\r\n\r\n,用来告知客户端响应头已传输完毕。 - Sec-WebSocket-Key提取不纯净:正则匹配请求头中的key时,没有过滤掉值末尾的换行、空格符,导致生成的
Sec-WebSocket-Accept值不符合要求。 - 单次写入不完整:
socket_write可能因为TCP缓冲区限制无法一次性写完所有响应数据,部分字节丢失会导致客户端识别不到完整握手响应。 - 主循环阻塞:主逻辑中
sleep(2)会阻塞连接处理,容易触发客户端超时。
修复代码
1. 修正握手函数逻辑
private function handshake($client) { if ($client instanceof Socket || is_resource($client)) { echo "== Client is Valid ==\n"; } // 读取客户端发送的请求 $read = socket_read($client, 2048); if ($read === false) { throw new Exception("Could not Read!!"); } // 修正正则,提取纯净的Sec-WebSocket-Key if (preg_match("/Sec-WebSocket-Key:\s*(.*?)\s*\r\n/", $read, $matches)){ // 确认sha1使用二进制输出 $accept_key = base64_encode(hash("sha1", $matches[1]."258EAFA5-E914-47DA-95CA-C5AB0DC85B11", true)); // 响应头末尾加两个\r\n $response_header = "HTTP/1.1 101 Switching Protocols\r\n". "Upgrade: websocket\r\n". "Connection: Upgrade\r\n". "Sec-WebSocket-Accept: $accept_key\r\n\r\n"; // 循环写入保证所有字节发送完成 $response_len = strlen($response_header); $total_sent = 0; while ($total_sent < $response_len) { $bytes_sent = socket_write($client, substr($response_header, $total_sent), $response_len - $total_sent); if ($bytes_sent === false) { echo "\n====== Cannot send the handshake ==========\n"; break; } $total_sent += $bytes_sent; } if ($total_sent == $response_len) { echo "\n====== handshake has been sent [". $total_sent ."bytes] ==========\n"; } } }
2. 修正主循环逻辑
移除不必要的sleep阻塞:
$running = true; $clients = []; while ($running) { if (count($clients) < 2) { $accept = socket_accept($this->socket); $this->handshake($accept); $clients[] = $accept; // 移除sleep(2),避免阻塞 continue; } // 后续需要添加socket_select逻辑处理已连接客户端的消息收发,否则连接会因为无响应被断开 }
后续注意事项
握手成功后,所有客户端和服务端之间的通信都需要遵循WebSocket帧格式,不能直接传输纯文本,需要自行实现帧的编码、解码逻辑,否则后续收发消息时依然会出现连接断开问题。
内容的提问来源于stack exchange,提问作者obeid salem
相关产品推荐
相关产品推荐

