基于Tungstenite的基础WebSocket服务器握手失败求助
问题分析与解决方案
核心问题
你遇到的HandshakeError::Failure(Protocol(HttparseError(Token)))错误,根源是客户端使用了加密WebSocket协议(wss://),但你的服务器是普通TCP的WebSocket服务(仅支持ws://),两者协议不匹配导致握手失败。
快速修复方案:修改客户端连接协议
直接把客户端代码里的wss://改成ws://即可:
<script type="application/javascript"> // 把 wss:// 替换为 ws:// var ws = new WebSocket("ws://127.0.0.1:3012"); ws.onopen = function () { console.log('Connection is open...') ws.send("Hello from client"); }; ws.onmessage = function (evt) { console.log(evt.data); }; ws.onclose = function () { console.log("Connection is closed..."); }; window.ws = ws; console.log('Client is ready') </script>
修改后重启客户端和服务器,即可正常建立连接。
进阶方案:让服务器支持WSS(加密WebSocket)
如果需要使用wss://,你需要给服务器配置TLS证书(测试环境可用自签名证书),步骤如下:
1. 生成自签名证书(测试用)
用OpenSSL生成证书和密钥:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
执行命令后按提示填写信息,生成key.pem和cert.pem文件,放到项目根目录。
2. 修改服务器代码支持TLS
更新Cargo.toml依赖:
[dependencies] tungstenite = { version = "0.20", features = ["native-tls"] } native-tls = "0.2"
修改服务器代码,用TLS包装TCP流:
use std::{net::TcpListener, thread::spawn}; use native_tls::{Identity, TlsAcceptor}; use tungstenite::{ accept_hdr, handshake::server::{Request, Response}, }; fn main() { // 加载证书和密钥 let cert = include_bytes!("cert.pem"); let key = include_bytes!("key.pem"); let identity = Identity::from_pkcs8(cert, key).unwrap(); let acceptor = TlsAcceptor::new(identity).unwrap(); let server = TcpListener::bind("127.0.0.1:3012").unwrap(); for stream in server.incoming() { let acceptor = acceptor.clone(); spawn(move || { // 用TLS包装TCP流 let stream = acceptor.accept(stream.unwrap()).unwrap(); let callback = |req: &Request, mut response: Response| { println!("Received a new ws handshake"); println!("The request's path is: {}", req.uri().path()); println!("The request's headers are:"); for (ref header, _value) in req.headers() { println!("* {}", header); } let headers = response.headers_mut(); headers.append("MyCustomHeader", ":)".parse().unwrap()); headers.append("SOME_TUNGSTENITE_HEADER", "header_value".parse().unwrap()); Ok(response) }; let mut websocket = accept_hdr(stream, callback).unwrap(); loop { let msg = websocket.read_message().unwrap(); if msg.is_binary() || msg.is_text() { websocket.write_message(msg).unwrap(); } } }); } }
此时客户端可以继续使用wss://127.0.0.1:3012连接(浏览器可能会提示证书不安全,测试环境下忽略即可)。
补充说明
- 你之前添加的
features = ["native-tls"]是正确的,但缺少TLS流的包装逻辑,所以没生效。 - 移除
accept_hdr改用accept无法解决问题,因为核心是协议不匹配,和回调无关。
内容的提问来源于stack exchange,提问作者RonyHe
相关产品推荐
相关产品推荐

