WebSocket

  • 前后端通信方式
  • 测试环境(以示例电脑为例)
  • Java代码
  • pom依赖
  • application.yml
  • Spring-boot项目入口
  • 添加WebSocket配置
  • 具体业务实现
  • Html
  • 启动项目
  • 项目demo

前后端通信方式

客户端与服务端通信方式:

  1. Http请求
  2. WebSocket

想实现前后端实时推送数据实现方式:

  1. http:前端添加定时器(setInterval),然后定时向服务器请求数据
    定时器实现–> setInterval(请求函数,间隔时间(单位毫秒))
    优点:实现简单,无需修改服务器结构
    缺点:前后端消息无法实时同步
  2. Websocket:前后端通过握手,然后保持连接,可前后端相互推送数据
    优点:数据可以实时推送
    缺点:需浏览器支持websocket,后端也需添加支持

测试环境(以示例电脑为例)

  1. jdk: JDK12
  2. Spring-boot: 2.2.1.RELEASE
  3. 浏览器:谷歌浏览器
  4. maven:3.6.2

Java代码

pom依赖

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.llc</groupId>
	<artifactId>boot-webSocket</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>boot-webSocket Maven Webapp</name>
	<url>http://maven.apache.org</url>
	
	<!-- Spring-boot依赖 -->
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.2.1.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>12</java.version><!-- 指定项目jdk版本 -->
	</properties>
	

	<dependencies>
		<!-- web项目 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- 引入webSocket依赖 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>
		<!-- junit依赖 -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>webSocket</finalName>
	</build>
</project>

application.yml

server: 
  port: 8080
  servlet:
    context-path: /webSocket
    
spring:
  mvc: 
    view:
      suffix: .html
  jmx:
    default-domain: ${server.servlet.context-path}

Spring-boot项目入口

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication	//	Spring-boot基本注解
public class SocketApplication {
	
	public static void main(String[] args) {
		SpringApplication.run(SocketApplication.class, args);
	}

}

添加WebSocket配置

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

/**
 * 	开启Websocket
 * @author linlvcao
 *
 */
@Component
public class WebSocketConfig {

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

具体业务实现

package com.llc.socket.socket;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@ServerEndpoint(value = "/socket/{token}")
@Component
public class WebSocketServer {
	// 	加入日志
	private Logger log = LoggerFactory.getLogger(getClass());
	// 	用来记录当前在线连接数。应该把它设计成线程安全的。
	private static int onlineCount = 0;
	// 	存储用户唯一	可改用缓存
	private static Map<String, Session> socketMap = new HashMap<String, Session>();
	// 	当前线程
	private Session session;
	// 	当前用户标识
	private String token;

	/**
	 * 链接启用
	 * 
	 * @param session
	 * @param token
	 */
	@OnOpen
	public void onOpen(Session session, @PathParam(value = "token") String token) {
		// 	用户信息标识添加
		this.session = session;
		//	可用来当成唯一标识
		this.token = token;
		// 	加入缓存
		//	原来存在这个token用户,则剔除用户
		Session temp = socketMap.get(token);
		if (temp != null && temp.isOpen()) {
			try {
				temp.close();
			} catch (IOException e) {
				log.info("用户:" + token + ",剔除失败!");
				e.printStackTrace();
			}
		}
		socketMap.put(token, session);
		// 更新在线人数 TODO	正式需使用线程安全记录
		WebSocketServer.onlineCount ++;

		log.info("有新窗口开始监听:" + token + ",当前在线人数为" + onlineCount);

	}

	/**
	 * 关闭链接
	 */
	@OnClose
	public void onClose() {
		// 从线程中移除
		socketMap.remove(this.token);
		// 更新在线人数 TODO	正式需使用线程安全记录
		WebSocketServer.onlineCount --;
		
		log.info("有一连接关闭!当前在线人数为" + onlineCount);
	}

	/**
	 * 当链接出现异常
	 */
	@OnError
	public void onError(Session session, Throwable ex) {
		log.error("发生错误:{},Session ID: {}", ex.getMessage(), session.getId());
	}

	/**
	 * 接收客户端发来的信息
	 * 
	 * @param session
	 * @param message
	 */
	@OnMessage
	public void onMessage(String message) {
		log.info("收到来自窗口" + token + "的信息:" + message);
		//	回复收到信息
		this.SendMessage(session, "收到消息;" + message);

	}
	
	/**
	 * 	给指定客户端发送消息
	 * @param session
	 * @param message
	 */
	public void SendMessage(Session session, String message) {
		try {
			session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId()));
		}catch (Exception e) {
			log.error("发送消息出错", e);
		}
	}
	
	/**
	 * 	群发消息
	 * @param message
	 */
	public void batchSendMessage(String message) {
		for (Entry<String, Session> entry : socketMap.entrySet()) {
			Session session = entry.getValue();
			if (session.isOpen()) {
				SendMessage(session, message);
			}else {//	已经关闭则移除
				socketMap.remove(entry.getKey());
			}
		}
	}

	/**
	 * 	给指定用户发送信息
	 * @param token
	 * @param message
	 */
	public void SendMessage(String token, String message) {
		Session session = socketMap.get(token);
		if (session != null) {
			SendMessage(session, message);
		}
	}
}

Html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Socket链接测试</title>
<script type="text/javascript" src="https://cdn.bootcss.com/vue/2.6.10/vue.js"></script>
<script type="text/javascript" src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>
<body>
<div id="myApp">
	<input v-model="subMsg"/>
	<button @click="subData">提交</button>
	<h2>接收到的消息</h2>
	<p v-for="i in list">{{i}}</p>
	<br>
</div>


<script type="text/javascript">

	var baseURL = getRootUrl() + "/";
	
	function getRootUrl() {
		var path = window.location.pathname.substring(1);
		var root = (path == '') ? '' : path.substring(0, path.indexOf('/'));
		return window.location.protocol + '//' + window.location.host + '/' + root;
	}
	
	var socke=null;
	
	
	
	
	var vm = new Vue({
		el: "#myApp",
		data(){
			return {
				subMsg:null,
				list:[]
			}
		},
		mounted(){
			if (typeof (WebSocket) == "undefined") {
		        console.log("遗憾:您的浏览器不支持WebSocket");
		    } else {
		    	console.log("恭喜:您的浏览器支持WebSocket");

		    	var that = this;
		        //	实现化WebSocket对象
		        //	指定要连接的服务器地址与端口建立连接
		        //	注意ws、wss使用不同的端口。我使用自签名的证书测试,
		        //	无法使用wss,浏览器打开WebSocket时报错
		        //	ws对应http、wss对应https。
		        var path = baseURL + "socket/" + "3jeioqejter";
		        //var path = "http://localhost:8080/socket/" + "3jeioqejter";
		        path = path.replace("http","ws");
		        socket = new WebSocket(path);
		        socket.onopen = function() {
		            console.log("Socket 已打开");
		            socket.send("消息发送测试(From Client)");
		        };
		        
		      	//	收到消息事件
		        socket.onmessage = function(msg) {
		            console.log(msg);
		            that.list.push(msg.data);
		        };
		        
		      	//	连接关闭事件
		        socket.onclose = function() {
		            console.log("Socket已关闭");
		        };
		        //	发生了错误事件
		        socket.onerror = function() {
		            alert("Socket发生了错误");
		        }

		        //	窗口关闭时,关闭连接
		        window.unload=function() {
		            socket.close();
		        };
		        
		    }
		},
		methods: {
			subData() {
				vm.sendMsg(vm.subMsg);
			},
			sendMsg(data) {//	发送消息
				socket.send(data);
			}
		}
	});
</script>

</body>
</html>

启动项目

访问地址:
http://localhost:8080/webSocket/socket.html

效果

Springboot整合rocketmq广播 springboot rsocket_maven