从零实现Rust WebSocket客户端遇连接升级响应问题求助
从零实现Rust WebSocket客户端的连接问题解决
问题现象
- 作为Rust新手,从零实现WebSocket客户端时,连接echo.websocket.org等回声服务器始终无法获取升级响应。
- 移除请求头中的
Upgrade: websocket字段后,能收到200响应,说明服务器可达且底层Socket通信正常。 - 尝试多个WebSocket回声服务器均不认可请求头;Postman可正常连接目标服务器,但复制其请求头仍无法解决问题。
排查与解决过程
- 先按照建议将base64随机数(nonce)的生成方式更新为与tungstenite一致的实现(引入
data-encoding依赖),但问题未解决。 - 最终定位核心问题:原代码使用
read_to_string等待EOF,但WebSocket服务器不会发送EOF,必须按\r\n逐行读取响应。改用tokio的BufReader处理输入流后,问题得到解决。
初始代码
use std::sync::Arc; use rand::Rng; use rustls::{pki_types::ServerName, ClientConfig, RootCertStore}; use tokio::{io::{AsyncReadExt, AsyncWriteExt}, net::{lookup_host, TcpSocket}}; use tokio_rustls::TlsConnector; #[tokio::main] async fn main() { let nonce = generate_nonce(); let host = "echo.websocket.org"; let port = 443; let path = "/"; let addr = format!("{}:{}", host, port); let addr = lookup_host(addr).await.unwrap().next().unwrap(); let socket = TcpSocket::new_v4().unwrap(); let mut stream = socket.connect(addr).await.unwrap(); let server_name = ServerName::try_from(host).unwrap(); let mut tls = get_tls_connector().connect(server_name, &mut stream).await.unwrap(); let mut write_string = String::new(); write_string.push_str(format!("GET {} HTTP/1.1\r\n", path).as_str()); write_string.push_str(format!("Host: {}\r\n", host).as_str()); write_string.push_str("Connection: Upgrade\r\n"); write_string.push_str("Upgrade: websocket\r\n"); write_string.push_str(format!("Sec-WebSocket-Key: {}\r\n", nonce).as_str()); write_string.push_str("Sec-WebSocket-Version: 13\r\n"); write_string.push_str("\r\n"); tls.write_all(write_string.as_bytes()).await.unwrap(); tls.flush().await.unwrap(); let mut read_str = String::new(); let _size = tls.read_to_string(&mut read_str).await.unwrap(); println!("{}", read_str); read_str.clear(); } fn generate_nonce() -> String { let r: [u8; 16] = rand::random(); data_encoding::BASE64.encode(&r) } fn get_tls_connector() -> TlsConnector { let mut root_store = RootCertStore::empty(); root_store.extend(webpki_roots::TLS_SERVER_ROOTS .iter() .cloned() ); let config = ClientConfig::builder() .with_root_certificates(root_store) .with_no_client_auth(); let rc_config = Arc::new(config); TlsConnector::from(rc_config) }
修改后的main函数
use std::sync::Arc; use rustls::{pki_types::ServerName, ClientConfig, RootCertStore}; use tokio::{io::{split, AsyncBufReadExt, AsyncWriteExt, BufReader}, net::{lookup_host, TcpSocket}}; use tokio_rustls::TlsConnector; #[tokio::main] async fn main() { let nonce = generate_nonce(); let host = "echo.websocket.org"; let port = 443; let path = "/"; let addr = format!("{}:{}", host, port); let addr = lookup_host(addr).await.unwrap().next().unwrap(); let socket = TcpSocket::new_v4().unwrap(); let mut stream = socket.connect(addr).await.unwrap(); let server_name = ServerName::try_from(host).unwrap(); let tls = get_tls_connector().connect(server_name, &mut stream).await.unwrap(); let (reader, mut writer) = split(tls); let mut write_string = String::new(); write_string.push_str(format!("GET {} HTTP/1.1\r\n", path).as_str()); write_string.push_str(format!("Host: {}\r\n", host).as_str()); write_string.push_str("Connection: Upgrade\r\n"); write_string.push_str("Upgrade: websocket\r\n"); write_string.push_str(format!("Sec-WebSocket-Key: {}\r\n", nonce).as_str()); write_string.push_str("Sec-WebSocket-Version: 13\r\n"); //write_string.push_str("Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits\r\n"); write_string.push_str("\r\n"); writer.write_all(write_string.as_bytes()).await.unwrap(); writer.flush().await.unwrap(); let mut lines = BufReader::new(reader).lines(); loop { let line = lines.next_line().await.unwrap().unwrap(); println!("{}", line); if line.is_empty() { break; } } }
内容的提问来源于stack exchange,提问作者codingNewb
相关产品推荐
相关产品推荐

