一、问题来源
一直以为WebSocket实现是在TCP协议层,直接可以用javascript
的ws直接访问java
的ServerSocket
进行通信。
- java端代码
private ServerSocket serverSocket;
public void start(int port) throws IOException {
serverSocket = new ServerSocket(port);
System.out.println("server is running port " + port);
while (true) {
Socket sock = serverSocket.accept();
System.out.println("connected from " + sock.getRemoteSocketAddress());
new TcpClientHandler(sock).start();
}
}
- JavaScript代码
const socket = new WebSocket('ws://localhost:8888/')
// Connection opened
socket.addEventListener('open', function (event) {
console.log('连接成功 ', event)
socket.send('Hello Server!')
})
java端能正常接受请求,但是却处理不了,同时js端报链接错误:从错误的日志来看,请求能到server端,但是没有握手成功,client接收不到请求。所以问题并不是那么简单,查阅了相关资料学习。
二、认识WebSocket
WebSocket是建立在TCP协议之上的应用层协议,并复用HTTP的握手通道,实现全双工通讯的网络技术。它支持双向通信。使用简单,只需要在浏览器中调用api就能完成协议切换。并支持扩展,可以在协议中实现自定义的子协议。相比http协议头部开销小,可以在网络中更快的传输。
相比TCP协议,WebSocket相对简单,只需要完成一次get请求,完成一次协议升级,这样就可以使用WebSocket通信。
连接过程:
ws://localhost:8888
发送get请求,请求协议转换,协议头:Connection:Upgrade
Upgrade:websocket
,表示协议升级,升级协议到websocket
。server端返回code 101代表,协议升级成功,后续通过双向通道传输数据即可。
三、示例代码
java原生代码,使用Java原生代码实现websocket服务的方法, 此方法需要引入一个第三方库java-websocket.jar
项目源代码位于: github.com/TooTallNate…
示例代码位于: github.com/TooTallNate…
如果你的项目使用gradle作为管理工具, 可以添加以下gradle依赖
implementation group: 'org.java-websocket', name: 'Java-WebSocket', version: '1.5.3'
如果你的项目使用maven进行管理, 可以添加以下maven依赖
mven依赖
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.5.3</version>
</dependency>
package com.mum.socket;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
public class SocketServer extends WebSocketServer {
public SocketServer(int port) throws UnknownHostException {
super(new InetSocketAddress(port));
}
public SocketServer(InetSocketAddress address) {
super(address);
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
conn.send("Welcome to the server!"); // This method sends a message to the new client
broadcast("new connection: " + handshake.getResourceDescriptor()); // This method sends a message to all clients
// connected
System.out.println(conn.getRemoteSocketAddress().getAddress().getHostAddress() + " entered the room!");
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
broadcast(conn + " has left the room!");
System.out.println(conn + " has left the room!");
}
@Override
public void onMessage(WebSocket conn, String message) {
broadcast(message);
System.out.println(conn + ": " + message);
}
@Override
public void onError(WebSocket conn, Exception ex) {
ex.printStackTrace();
if (conn != null) {
// some errors like port binding failed may not be assignable to a specific
// websocket
}
}
@Override
public void onStart() {
System.out.println("Server started!");
//setConnectionLostTimeout(0);
setConnectionLostTimeout(100);
}
}
package com.mum.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class SocketServerTest {
public static void main(String[] args) throws InterruptedException, IOException {
int port = 8888; // 843 flash policy port
SocketServer s = new SocketServer(port);
s.start();
System.out.println("ChatServer started on port: " + s.getPort());
BufferedReader sysin = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String in = sysin.readLine();
s.broadcast(in);
if (in.equals("exit")) {
s.stop(1000);
break;
}
}
}
}
js代码实现
<!DOCTYPE html>
<html lang="zh-CN">
<header>
<title>js code</title>
</header>
<style>
.container {
width: 100%;
position: relative;
}
.margintLeft10 {
margin-left: 10px;
}
</style>
<body>
<div class="container">
<button class="margintLeft10" onclick="socketTest()">socketTest</button>
<button class="margintLeft10" onclick="sendDate()">sendDate</button>
</div>
<script>
let socket;
function socketTest() {
socket = new WebSocket('ws://localhost:8888/')
// Connection opened
socket.addEventListener('open', function (event) {
console.log('连接成功 ', event)
socket.send('Hello Server!')
})
// Listen for messages
socket.addEventListener('message', function (event) {
console.log('Message from server ', event.data)
})
// 监听错误事件
socket.addEventListener('error', function (event) {
console.log('error', event)
})
// 监听关闭事件
socket.addEventListener('close', function (event) {
console.log('close', event)
})
}
function sendDate() {
if (socket) {
socket.send('现在时间: ' + new Date());
} else {
console.log('socket 未创建');
}
}
</script>
</body>
</html>
node js实现更简单 借助 ws 库
const app = require("express")()
const server = require("http").Server(app)
const WebSocket = require("ws")
const MyWs = new WebSocket.Server({ port: 8080 })
MyWs.on("open", () => {
console.log("connected")
})
MyWs.on("close", () => {
console.log("disconnected")
})
MyWs.on("connection", (ws, req) => {
const port = req.connection.remotePort
const clientName = port
console.log(`${clientName} is connected`)
// 发送欢迎信息给客户端
ws.send("Welcome :" + clientName + "加入聊天室")
ws.on("message", (message) => {
// 广播消息给所有客户端
MyWs.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(clientName + " -> " + message)
}
})
})
})
app.get("/", (req, res) => {
res.sendfile(__dirname + "/index.html")
})
app.listen(8888)
参考资料
WebSocket:5分钟从入门到精通 juejin.cn/post/684490…
java实现websocket的五种方式 www.cnblogs.com/guoapeng/p/…