全新揭秘:Java WebSocket全双工通信的实践与运用
- 一、简介
- 何为全双工通信全双工?
- WebSocket的使用场景
- 二、如何使用Java实现WebSocket
- 1,引用websocket相关starter
- 2,启用websocket
- 3,服务端代码开发
- 4,群发测试接口
- 5,前端代码
- 三、测试验证
- 四、总结
一、简介
WebSocket是一种强大的跨平台和全双工通信的网络技术。它可以在客户端和服务器之间进行全双工通信,从而在真实的服务器推送场景中提供极好的性能和一致性。
何为全双工通信全双工?
全双工模式是指两端系统可以同时发送和接收信息,即客户端可以向服务端发起请求,并接收服务端的响应, 同时,服务端也可以主动向客户端发起请求,并接收客户端的响应。 而我们最常见的web应用,都属于请求-响应,属于半双工模式, 这种模式下,服务端是不能主动请求客户端的。
WebSocket提供了这种全双工通信方式,使得服务器可以随时把信息推送给客户端,客户端也能随时向服务器发送信息,减少了通信的延迟时间。
WebSocket的使用场景
这种全双工的通信方式,使得WebSocket在一些特殊场景下有着无比的优势:
1,实时应用:聊天室应用,多人协作的应用。
2,游戏应用:浏览器游戏或者手机app游戏等,他们需要在服务端和客户端之间保持低延迟、全双工和实时的数据交换。
3,实时展现:对于需要频繁更新的应用,WebSocket提供了一种高效的解决方案。
以及无需客户端提供公网IP,服务端即可以访问客户端的某些特殊场景(B2B场景下),比如服务端对接了多家银行,支付时又依赖于客户端插入的U盾。
二、如何使用Java实现WebSocket
Java EE7的APIs支持WebSocket协议,因此我们使用Java来实现WebSocket变得非常简单。以下以一个springboot web应用中,使用java WebSocket进行全双工通信示例:
1,引用websocket相关starter
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2,启用websocket
在springboot启动类中,增加如下代码:
// 启用websocket
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
3,服务端代码开发
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* WebSocket测试服务
*/
@Component // 也需要交于spring容器管理
@ServerEndpoint("/ws/{userId}") // 服务地址
public class TestWebSocketServer {
//长连接,即客户端会话对象
private static Map<String, Session> clientMap = new HashMap();
/**
* 响应客户端socket open事件,接收客户端请求,建立连接
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
System.out.println("客户端:" + userId + "建立连接");
clientMap.put(userId, session);
}
/**
* 用来处理客户端发送消息事件,接收消息并处理
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("userId") String userId) throws IOException {
System.out.println("收到来自客户端:" + userId + "的信息:" + message);
Session session = clientMap.get(userId);
session.getBasicRemote().sendText(userId + " 你好,已收到你发送的消息:" + message);
}
/**
* 响应客户端连接关闭事件调用的方法
*
* @param userId
*/
@OnClose
public void onClose(@PathParam("userId") String userId) {
System.out.println("连接断开:" + userId);
clientMap.remove(userId);
}
/**
* 测试群发消息,实现业务场景根据业务需要,发给特定的客户端,以实现客户端无刷新自动显示最新数据,比如下单被接单,客户催单提醒等。
*
* @param message
*/
public void sendMessagesToALlUsers(String message) {
Collection<Session> sessions = clientMap.values();
for (Session session : sessions) {
try {
//服务器向客户端发送消息
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4,群发测试接口
@GetMapping("/ws/sendAll")
public String sendToAllClients() {
testWebSocketServer.sendMessagesToALlUsers("群发消息测试");
return "success";
}
5,前端代码
创建一个html页面,命名ws_demo.html,代码如下:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demo</title>
</head>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">断开连接</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
var clientId = "10000"; // 模拟的用户ID,实际业务场景可以用当前登录的用户的唯一标识
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
//连接WebSocket节点
websocket = new WebSocket("ws://localhost:9600/ws/"+clientId);
}
else{
alert('Not support websocket')
}
//连接发生错误前端回调方法,比如服务端挂了
websocket.onerror = function(){
setMessageInnerHTML("与服务端连接出错");
};
//连接成功建立的回调方法
websocket.onopen = function(){
setMessageInnerHTML("与服务端建立连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
}
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("已关闭");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
closeWebSocket();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
//关闭连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>
三、测试验证
springboot后端应用启动:
打开前端页面
前端如果打开后,不能正常显示 ,可以将前端部署在nginx上,直接放到nginx的html目录下即可,然后通过浏览器访问http://localhost/ws_demo.html 即可显示页面如下。
客户端主动发送消息
服务端收到客户端响应
服务端主动向客户端群发测试
客户端断开(浏览器关闭)时,会触发onclose事件,服务端也会感知到; 同时服务端关闭时,也会触发服务端onclose事件,客户端也会被通知到,大家可以测试验证下。
以上测试展示了一个基于java websocket的全双工网络通信的具体示例,实现客户端和服务器之间进行全双工通信非常简便,在真实的服务器推送场景中只需扩展示例,实现具体的业务即可。
四、总结
总的来看,WebSocket带来了客户端和服务端通信的新可能,特别是在需要服务端主动推送消息给客户端的应用场景中,其出色的实时性和低延迟性提供了前所未有的用户体验。Java作为一门全能的编程语言,也为我们使用WebSocket提供了完善的工具。使用Java去实现WebSocket应用,在实时性、低延迟和全双工这些方面,将会非常高效。