JS WebSocket连接Java ServerSocket失败:两端报错原因排查
你遇到的问题本质是JavaScript端用的是WebSocket协议,而Java端只是普通的TCP Socket服务器,两者的通信规则完全不兼容,导致两边都报错。
具体原因分析
JavaScript端的超时错误
你用wss://127.0.0.1:1234/创建WebSocket连接时,浏览器会先发送一段WebSocket握手请求(HTTP格式的升级请求),但你的JavaServerSocket只是普通TCP服务器,根本不懂WebSocket的握手规则,不会返回对应的响应,浏览器等不到握手回应就会超时断开,所以出现WebSocket opening handshake timed out错误。
Java端的EOFException
你的Java代码直接用DataInputStream.readUTF()读取数据,但WebSocket发送的不是Java特定的UTF编码格式(Java的readUTF()要求先读两个字节的长度标识,再读内容)。而且在WebSocket握手完成前,浏览器发送的是握手帧,不是你预期的文本数据,readUTF()尝试读取不符合格式的内容,最终因为连接断开(浏览器超时关闭)导致读到流末尾,抛出EOFException。
解决方案:统一通信协议
因为浏览器环境下无法直接使用普通TCP连接,所以推荐把Java端改成WebSocket服务器,和JavaScript端的WebSocket协议匹配。
修改后的Java WebSocket服务器示例
可以用成熟的Java WebSocket库,比如Java-WebSocket(轻量易用),示例代码如下:
package networkingtest; import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; import org.java_websocket.server.WebSocketServer; import java.net.InetSocketAddress; public class WebSocketServerExample extends WebSocketServer { public WebSocketServerExample(int port) { super(new InetSocketAddress(port)); } // 客户端连接成功时触发 @Override public void onOpen(WebSocket conn, ClientHandshake handshake) { System.out.println("新客户端接入: " + conn.getRemoteSocketAddress()); conn.send("已成功连接到服务器"); } // 客户端断开连接时触发 @Override public void onClose(WebSocket conn, int code, String reason, boolean remote) { System.out.println("客户端断开: " + conn.getRemoteSocketAddress()); } // 收到客户端消息时触发 @Override public void onMessage(WebSocket conn, String message) { System.out.println("收到消息: " + message); // 回复客户端 conn.send("服务器已收到你的消息: " + message); } // 发生错误时触发 @Override public void onError(WebSocket conn, Exception ex) { ex.printStackTrace(); } // 服务器启动时触发 @Override public void onStart() { System.out.println("WebSocket服务器已启动,监听端口: " + getPort()); } public static void main(String[] args) { WebSocketServerExample server = new WebSocketServerExample(1234); server.start(); } }
依赖配置(Maven)
如果用Maven管理项目,需要在pom.xml中添加Java-WebSocket的依赖:
<dependency> <groupId>org.java-websocket</groupId> <artifactId>Java-WebSocket</artifactId> <version>1.5.3</version> </dependency>
修改后的JavaScript代码
先使用ws://(非加密WebSocket)测试,wss://需要配置SSL证书,后续可以再扩展:
var connection; function init() { // 改用ws://协议,和Java服务器匹配 connection = new WebSocket('ws://127.0.0.1:1234/'); connection.onopen = function () { console.log("连接成功!"); connection.send('Ping'); }; // 监听服务器发送的消息 connection.onmessage = function(event) { console.log("服务器回复: ", event.data); }; // 监听错误 connection.onerror = function(error) { console.error("连接出错: ", error); }; // 监听连接关闭 connection.onclose = function(event) { console.log("连接已关闭: ", event); }; }
总结
WebSocket是基于TCP的应用层协议,有自己的握手、帧解析规则,不能直接用普通TCP Socket服务器来处理。只要把Java端改成WebSocket服务器实现,两端就能正常通信了。
内容的提问来源于stack exchange,提问作者amsjntz

