WebSocket握手哈希编码错误问题排查(Delphi实现)
解决WebSocket握手失败:Sec-WebSocket-Accept生成错误的问题
嘿,我一眼就看出问题出在哪了——你现在生成Sec-WebSocket-Accept的方式完全不符合WebSocket协议要求,导致浏览器那边握手失败。咱们一步步来修复它:
核心错误原因
WebSocket协议明确要求:把客户端发来的Sec-WebSocket-Key加上固定魔法字符串258EAFA5-E914-47DA-95CA-C5AB0DC85B11后,计算SHA1的二进制哈希值,再对这个二进制数据做Base64编码。但你的代码现在是把SHA1生成的十六进制字符串当成原始数据来Base64编码,这就导致结果完全不对,握手自然失败。
你的代码里的具体问题
- 哈希函数逻辑错误:
THashSHA1.GetHashString返回的是哈希值的十六进制文本(比如c289980eb715b3e19107152dc21f075c9f7bf539),但我们需要的是哈希计算后得到的原始二进制字节流,不是文本形式。 - 内存泄漏:
THashSHA1.Create后没有释放实例,长期运行会导致内存问题。
修正后的完整代码
第一步:修复SHA1哈希生成函数
我们需要获取SHA1的二进制结果,而不是十六进制字符串:
function TServerForm.GenStrHashSHA1(const Str: string): TBytes; var HashSHA: THashSHA1; begin HashSHA := THashSHA1.Create; try // 针对Unicode Delphi(2009+),直接用字符串的原始字节计算哈希 HashSHA.Update(Str[1], Length(Str) * SizeOf(Char)); // 初始化结果数组大小(SHA1哈希是20字节) SetLength(Result, HashSHA.HashSize div 8); // 获取二进制哈希值 HashSHA.GetHashBytes(Result); finally // 释放哈希实例,避免内存泄漏 HashSHA.Free; end; end;
第二步:修正握手响应头发送方法
现在用二进制哈希来生成正确的Base64编码的Accept值:
procedure TServerForm.SendHeader(const Key: string); var HashBytes: TBytes; AcceptKey: string; const MAGIC_STRING = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'; begin // 拼接密钥和魔法字符串,注意要Trim掉密钥的前后空格 HashBytes := GenStrHashSHA1(Trim(Key) + MAGIC_STRING); // 对二进制哈希进行Base64编码,这才是正确的处理方式 AcceptKey := TNetEncoding.Base64.EncodeBytesToString(HashBytes); // 发送标准的WebSocket握手响应 CliSocket.SendStr( 'HTTP/1.1 101 Switching Protocols' + #13#10 + 'Upgrade: websocket' + #13#10 + 'Connection: Upgrade' + #13#10 + 'Sec-WebSocket-Accept: ' + AcceptKey + #13#10 + #13#10 // 空行表示响应头结束 ); Memo2.Lines.Add('Header was sent'); end;
额外注意事项
如果你的Delphi是旧版ANSI版本(2009之前),需要先把字符串转成UTF-8再计算哈希,因为WebSocket协议要求用UTF-8编码处理密钥。可以用TEncoding.UTF8.GetBytes(Str)来转换后再传入哈希函数。
比如你测试用的密钥Nk1L3oS2Q7LHdoGP2Uyn7Q==,用修正后的代码生成的Accept值会符合浏览器的预期,握手就能成功了。
内容的提问来源于stack exchange,提问作者Hugo
相关产品推荐
相关产品推荐

