Java 中的 WebSocket
Java JavaScript WebSocket About 5,239 words介绍
握手阶段基于HTTP
。握手成功后升级为TCP
。
HTTP/1.1 101
:表示使用HTTP
的1.1
版本且HTTP
状态码为101
。Connection: Upgrade
:表示链接升级了。Upgrade: websocket
:表示升级为websocket
了(即长链接)。Sec-WebSocket-Version
:告诉服务端使用的WebSocket Draft
协议版本号。Sec-WebSocket-Key
:浏览器随机生成的一个Base64
字符串密钥。Sec-WebSocket-Accept
:服务端返回的验证密钥。
请求头
GET ws://localhost:10000/test/ws/testusername HTTP/1.1
Host: localhost:10000
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36 Edg/89.0.774.77
Upgrade: websocket
Origin: http://localhost:10000
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cookie: JSESSIONID=B0C9AA2839BED68667B14332FC1459DF
Sec-WebSocket-Key: BDf7DVkhHpGGIZYhvxj20A==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
响应头
HTTP/1.1 101
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: MKTv1hNVb4gVXQ8l/rbhsbjIW0U=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
Date: Tue, 20 Apr 2021 13:16:16 GMT
特点
- 长链接。
- 不受浏览器跨域限制。
- 若开启了
Tomcat
服务,WebSocket
端口与Tomcat
端口一致。
示例代码
Java
添加依赖
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
WebSocket
实例
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
@ServerEndpoint(value = "/test/ws/{username}")
public class WebSocketServer {
// 记录当前连接数
private static final AtomicLong COUNT = new AtomicLong(0);
// 保存用户名与 WebSocketSession 间的映射,有助于服务端主动推送
private static final ConcurrentHashMap<String, Session> map = new ConcurrentHashMap<>(1 << 10);
@OnOpen
public void onOpen(@PathParam("username") String username, Session session) {
map.putIfAbsent(username, session);
COUNT.incrementAndGet();
// 最大超时时间 60 秒
session.setMaxIdleTimeout(TimeUnit.MINUTES.toMillis(5));
session.setMaxBinaryMessageBufferSize(8192 * 1024); // 8KB
session.setMaxTextMessageBufferSize(8192 * 1024); // 字符数
session.getAsyncRemote().sendText("123");
}
@OnClose
public void onClose(@PathParam("username") String username, Session session, CloseReason closeReason) {
map.remove(username, session);
System.out.printf("onClose username#%s Session#%s closed because of %s%n", username,session.toString(), closeReason);
}
@OnError
public void onError(Session session, Throwable t) {
System.out.println("onError#" + session);
t.printStackTrace();
}
@OnMessage
public String onMessage(@PathParam("username") String username, String message, Session session) {
System.out.println("onMessage username#" + username + "#" + message);
return message + "#" + ThreadLocalRandom.current().nextInt(1 << 20);
}
}
JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>WebSocket-Test</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.js"></script>
</head>
<body>
<script>
var socket;
if (!window.WebSocket) {
alert("不支持WebSocket");
} else {
socket = new WebSocket("ws://" +window.location.host+ "/test/ws/testusername");
socket.onmessage = function (ev) {
var resText = document.getElementById("resText");
resText.value = resText.value + "\n" + ev.data;
};
socket.onopen = function (ev) {
var resText = document.getElementById("resText");
resText.value = "连接开启!";
};
socket.onclose = function (ev) {
var resText = document.getElementById("resText");
resText.value = resText.value + "\n" + "连接关闭!";
}
}
function send(message) {
if (!window.WebSocket) {
return;
}
if (socket.readyState === WebSocket.OPEN) {
socket.send(message);
} else {
alert("连接尚未打开!");
}
}
</script>
<form onsubmit="return false">
<textarea name="message" id="" cols="30" rows="10"></textarea>
<input type="button" value="发送" onclick="send(this.form.message.value)">
<h3>服务端输出</h3>
<textarea name="" id="resText" cols="30" rows="10"></textarea>
<input type="button" onclick="javascript:document.getElementById('resText').value('')" value="清空">
</form>
</body>
</html>
Tomcat WebSocket 加载流程
通过WsSci
对添加了@ServerEndpoint
注解的类进行加载。
@ServerEndpoint
标注的类不是单例的。
参考
https://tools.ietf.org/html/rfc6455
https://stackoverflow.com/questions/18265128/what-is-sec-websocket-key-for
https://tomcat.apache.org/tomcat-9.0-doc/web-socket-howto.html
https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/HomeWebsocket/WebsocketHome.html
Views: 3,102 · Posted: 2021-04-20
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓
Loading...