使用Postman/Insomnia连接Erlang Websocket时出现解析错误
问题根源与解决方案
你遇到的问题本质是协议不匹配:
- 你编写的是一个普通TCP echo服务器,仅处理原始二进制数据的收发
- 但你用Postman/Insomnia的WebSocket客户端发起连接,WebSocket是基于HTTP握手的应用层协议,需要服务器先完成握手流程,你的TCP服务器完全没有处理这一步,导致客户端握手失败,触发「Parse Error: Expected HTTP/」,随后主动关闭连接,所以服务器端收到
{error,closed}。
验证TCP服务器功能(如果仅测试TCP)
如果你的目标是测试这个TCP echo服务器,不要用WebSocket客户端,改用纯TCP客户端:
- 使用
telnet命令:telnet localhost 8091 - 或者
nc命令:nc localhost 8091
输入任意内容,服务器会原样返回;输入包含quit的内容,连接会关闭,这完全符合代码预期。
实现WebSocket服务器的两种方式
如果需要支持WebSocket连接,有两种常见方案:
方案1:使用成熟的Erlang Web框架(推荐)
Erlang生态中最常用的是cowboy框架,它原生支持WebSocket。步骤大致如下:
- 添加cowboy依赖到你的项目(比如用rebar3)
- 编写WebSocket处理模块,示例代码:
-module(my_ws_handler). -behaviour(cowboy_websocket_handler). -export([init/3]). -export([websocket_init/3]). -export([websocket_handle/3]). -export([websocket_info/3]). -export([websocket_terminate/3]). init({tcp, http}, _Req, _Opts) -> {upgrade, protocol, cowboy_websocket}. websocket_init(_TransportName, Req, _Opts) -> {ok, Req, undefined_state}. websocket_handle({text, <<"quit">>}, Req, State) -> {shutdown, Req, State}; websocket_handle({text, Msg}, Req, State) -> {reply, {text, Msg}, Req, State}; websocket_handle(_Data, Req, State) -> {ok, Req, State}. websocket_info(_Info, Req, State) -> {ok, Req, State}. websocket_terminate(_Reason, _Req, _State) -> ok.
- 配置并启动cowboy服务器,将WebSocket路由指向这个处理模块。
方案2:手动实现WebSocket握手(仅用于学习)
如果想手动处理WebSocket握手流程,需要解析客户端的HTTP请求,验证Upgrade头,生成符合规范的响应,然后切换到WebSocket帧协议。简化版的握手处理示例(仅处理基础握手,不支持子协议、扩展等):
handle_websocket_handshake(Socket) -> case gen_tcp:recv(Socket, 0) of {ok, Data} -> % 解析HTTP请求,提取Sec-WebSocket-Key case re:run(Data, "Sec-WebSocket-Key: (.*)\r\n", [{capture, [1], binary}]) of {match, [Key]} -> % 生成握手响应 Accept = base64:encode(crypto:hash(sha, <<Key/binary, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11">>)), Response = <<"HTTP/1.1 101 Switching Protocols\r\n", "Upgrade: websocket\r\n", "Connection: Upgrade\r\n", "Sec-WebSocket-Accept: ", Accept/binary, "\r\n\r\n">>, gen_tcp:send(Socket, Response), % 握手完成后,开始处理WebSocket帧(需要解析帧格式) handle_websocket_frames(Socket); _ -> gen_tcp:close(Socket) end; _ -> gen_tcp:close(Socket) end. handle_websocket_frames(Socket) -> % 这里需要实现WebSocket帧的解析和组装逻辑,比如处理文本帧、二进制帧等 % 示例仅做简单接收(实际需要解析帧头) case gen_tcp:recv(Socket, 0) of {ok, _Frame} -> % 假设收到文本帧,返回相同内容(需要组装成WebSocket帧格式) gen_tcp:send(Socket, <<1:1, 0:3, 1:4, 0:1, 5:7, "hello">>), % 示例帧:文本帧,长度5,内容hello handle_websocket_frames(Socket); _ -> gen_tcp:close(Socket) end.
然后修改handle/1函数,先判断是否是WebSocket握手请求,再调用对应的处理函数。
内容的提问来源于stack exchange,提问作者Gradie Mayombe
相关产品推荐
相关产品推荐

