SpringBoot实现WebSocket

一、什么是websocket

WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
Websocket是一个持久化的协议

二、新建SpringBoot工程

SpringBoot实现WebSocket_第1张图片
按照上图这样配置即可,点击下一步
因为本文章只实现websocket功能,所以只勾选SpringWeb,同时要保证springboot版本要低于3.0
SpringBoot实现WebSocket_第2张图片

创建完的工程目录如下
SpringBoot实现WebSocket_第3张图片

三、修改配置文件

将application.properties修改为application.yml,然后修改内容为

#服务器配置
server:
  port: 8081
  servlet:
    context-path: /api

SpringBoot实现WebSocket_第4张图片

修改POM.xml,在dependencies下加入所需依赖,加入后在空白处右键点击重新加载项目,下载对应依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-websocket</artifactId>
	<version>5.2.8.RELEASE</version>
</dependency>
<!--工具类 -->
	<dependency>
	<groupId>cn.hutool</groupId>
	<artifactId>hutool-all</artifactId>
	<version>5.4.1</version>
</dependency>

新建一个websocket包,包中新建两个类,代码如下

package com.example.demo.websocket;

import cn.hutool.json.JSONUtil;
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.concurrent.ConcurrentHashMap;

@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocket {
	private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();
	//实例一个session,这个session是websocket的session
	private Session session;

	//新增一个方法用于主动向客户端发送消息
	public static void sendMessage(Object message, String userId) {
		WebSocket webSocket = webSocketMap.get(userId);
		if (webSocket != null) {
			try {
				webSocket.session.getBasicRemote().sendText(JSONUtil.toJsonStr(message));
				System.out.println("【websocket消息】发送消息成功,用户="+userId+",消息内容"+message.toString());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static ConcurrentHashMap<String, WebSocket> getWebSocketMap() {
		return webSocketMap;
	}

	public static void setWebSocketMap(ConcurrentHashMap<String, WebSocket> webSocketMap) {
		WebSocket.webSocketMap = webSocketMap;
	}

	//前端请求时一个websocket时
	@OnOpen
	public void onOpen(Session session, @PathParam("userId") String userId) {
		this.session = session;
		webSocketMap.put(userId, this);
		sendMessage("CONNECT_SUCCESS", userId);
		System.out.println("【websocket消息】有新的连接,连接id"+userId);
	}

	//前端关闭时一个websocket时
	@OnClose
	public void onClose() {
		webSocketMap.remove(this);
		System.out.println("【websocket消息】连接断开,总数:"+ webSocketMap.size());
	}

	//前端向后端发送消息
	@OnMessage
	public void onMessage(String message) {
		if (!message.equals("ping")) {
			System.out.println("【websocket消息】收到客户端发来的消息:"+message);
		}
	}
}

package com.example.demo.websocket;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

//开启WebSocket的支持,并把该类注入到spring容器中
@Configuration
public class WebSocketConfig {

	@Bean
	public ServerEndpointExporter serverEndpointExporter() {
		return new ServerEndpointExporter();
	}

}

四、项目完整结构如下

SpringBoot实现WebSocket_第5张图片

五、测试功能

点击运行按钮,可以看到控制台对应的输出,输出看到JVM running for 表示启动成功
SpringBoot实现WebSocket_第6张图片

在桌面新建一个websocket.html,将代码复制进去

DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<title>本地websocket测试title>
		<meta name="robots" content="all" />
		<meta name="keywords" content="本地,websocket,测试工具" />
		<meta name="description" content="本地,websocket,测试工具" />
		<style>
			.btn-group{
				display: inline-block;
			}
		style>
	head>
	<body>
		<input type='text' value='通信地址, ws://开头..' class="form-control" style='width:390px;display:inline'
		 id='wsaddr' />
		<div class="btn-group" >
			<button type="button" class="btn btn-default" onclick='addsocket();'>连接button>
			<button type="button" class="btn btn-default" onclick='closesocket();'>断开button>
			<button type="button" class="btn btn-default" onclick='$("#wsaddr").val("")'>清空button>
		div>
		<div class="row">
			<div id="output" style="border:1px solid #ccc;height:365px;overflow: auto;margin: 20px 0;">div>
			<input type="text" id='message' class="form-control" style='width:810px' placeholder="待发信息" onkeydown="en(event);">
			<span class="input-group-btn">
				<button class="btn btn-default" type="button" onclick="doSend();">发送button>
			span>
			div>
		div>
	body>		
		
		<script src="https://code.jquery.com/jquery-3.1.1.min.js">script>
		<script language="javascript" type="text/javascript">
			function formatDate(now) {
				var year = now.getFullYear();
				var month = now.getMonth() + 1;
				var date = now.getDate();
				var hour = now.getHours();
				var minute = now.getMinutes();
				var second = now.getSeconds();
				return year + "-" + (month = month < 10 ? ("0" + month) : month) + "-" + (date = date < 10 ? ("0" + date) : date) +
					" " + (hour = hour < 10 ? ("0" + hour) : hour) + ":" + (minute = minute < 10 ? ("0" + minute) : minute) + ":" + (
						second = second < 10 ? ("0" + second) : second);
			}
			var output;
			var websocket;
 
			function init() {
				output = document.getElementById("output");
				testWebSocket();
			}
 
			function addsocket() {
				var wsaddr = $("#wsaddr").val();
				if (wsaddr == '') {
					alert("请填写websocket的地址");
					return false;
				}
				StartWebSocket(wsaddr);
			}
 
			function closesocket() {
				websocket.close();
			}
 
			function StartWebSocket(wsUri) {
				websocket = new WebSocket(wsUri);
				websocket.onopen = function(evt) {
					onOpen(evt)
				};
				websocket.onclose = function(evt) {
					onClose(evt)
				};
				websocket.onmessage = function(evt) {
					onMessage(evt)
				};
				websocket.onerror = function(evt) {
					onError(evt)
				};
			}
 
			function onOpen(evt) {
				writeToScreen("连接成功,现在你可以发送信息啦!!!");
			}
 
			function onClose(evt) {
				writeToScreen("websocket连接已断开!!!");
				websocket.close();
			}
 
			function onMessage(evt) {
				writeToScreen('服务端回应 ' + formatDate(new Date()) + '
' + evt.data + ''
); } function onError(evt) { writeToScreen('发生错误: ' + evt.data); } function doSend() { var message = $("#message").val(); if (message == '') { alert("请先填写发送信息"); $("#message").focus(); return false; } if (typeof websocket === "undefined") { alert("websocket还没有连接,或者连接失败,请检测"); return false; } if (websocket.readyState == 3) { alert("websocket已经关闭,请重新连接"); return false; } console.log(websocket); $("#message").val(''); writeToScreen('你发送的信息 ' + formatDate(new Date()) + '
'
+ message); websocket.send(message); } function writeToScreen(message) { var div = "
" + message + "
"
; var d = $("#output"); var d = d[0]; var doScroll = d.scrollTop == d.scrollHeight - d.clientHeight; $("#output").append(div); if (doScroll) { d.scrollTop = d.scrollHeight - d.clientHeight; } } function en(event) { var evt = evt ? evt : (window.event ? window.event : null); if (evt.keyCode == 13) { doSend() } }
script> html>

双击打开这个html文件,页面的功能如下,websocket的通信地址要以ws://开头,因为我的springboot启动在8081端口,所以我的地址就是 ws://localhost:8081/api/websocket/{连接id},websocket前面加了api是前面在yml文件中配置了context-path,没有配置可以去掉。实际项目中,将连接id换成唯一的用户id即可向指定用户发送消息。
SpringBoot实现WebSocket_第7张图片

效果如下
SpringBoot实现WebSocket_第8张图片

这时候服务器控制台输出为
SpringBoot实现WebSocket_第9张图片

到此,SpringBoot搭建WebSocket就已经完成,还有什么问题可以私信作者~

你可能感兴趣的:(SpringBoot,websocket,spring,boot,java)