利用spring boot开发websocket,spring boot环境要做好,没做好也没关系,本章节,将从零开始搭建。
话不多说,直接上代码。
一、对于有spring boot环境的开发步骤。没有环境的拉到最下面,有搭建环境的pom和application配置,至于怎么新建maven工程,应该不用说了吧,默认你们看本贴吧的都会了- -!:
1、创建WebSocketConfig
package com.bm.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @author: zxy
* @data: 2020年3月18日 下午5:07:24
* @description: 类的描述
**/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
2、创建WebSocketServer
package com.bm.service;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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;
import org.springframework.util.StringUtils;
import com.bm.utils.AddOnlineCount;
import com.bm.utils.SubOnlineCount;
/**
* @author: zxy
* @data: 2020年3月18日 下午4:59:48
* @description: 类的描述
**/
@ServerEndpoint("/websocket/{userId}")
@Component
public class WebSocketServer {
/**
* 日志对象
*/
protected Logger log = LoggerFactory.getLogger(getClass());
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
public static int onlineCount;
// 创建一个线程安全的map,正式上线,把消息记录放进缓存更好一些,当所有用户退出时,清除缓存
private static Map<String, WebSocketServer> users = Collections.synchronizedMap(new HashMap());
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
// 放入map中的key,用来表示该连接对象
private String username;
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String username) {
this.session = session;
this.username = username;
users.put(username, this); // 加入map中,为了测试方便使用username做key
AddOnlineCount addOnlineCount = new AddOnlineCount(session, username, onlineCount);
Thread usernameThread = new Thread(addOnlineCount, username);
usernameThread.start();
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
users.remove(this.username); // 从set中删除
SubOnlineCount subOnlineCount = new SubOnlineCount(username, onlineCount);
Thread usernameThread = new Thread(subOnlineCount, username);
usernameThread.start();
}
/**
* 收到客户端消息后触发的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message) {
log.info("来自客户端的消息:" + message);
// 群发消息
try {
if (StringUtils.isEmpty(message)) {
return;
}
sendInfo(username + ": " + message);
} catch (IOException e) {
e.printStackTrace();
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误 session: " + session);
error.printStackTrace();
}
/**给特定人员发送消息
* @param username
* @param message
* @throws IOException
*/
public void sendMessageToSomeBody(String username, String message) throws IOException {
if (users.get(username) == null) {
return;
}
users.get(username).session.getBasicRemote().sendText(message);
this.session.getBasicRemote().sendText(this.username + "@" + username + ": " + message);
}
/**
* 群发自定义消息
*/
public void sendInfo(String message) throws IOException {
for (WebSocketServer item : users.values()) {
try {
item.session.getBasicRemote().sendText(message);
} catch (IOException e) {
continue;
}
}
}
}
3、创建AddOnlineCount(增加在线人数)、SubOnlineCount(减少在线人数)、SpringContextUtil
package com.bm.utils;
import java.io.IOException;
import javax.websocket.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bm.service.WebSocketServer;
/**
* @author: zxy
* @data: 2020年3月19日 上午9:00:27
* @description: 将在线人数变量设置为线程安全的,增加在线人数
**/
public class AddOnlineCount extends Thread {
/**
* 日志对象
*/
protected Logger log = LoggerFactory.getLogger(getClass());
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 放入map中的key,用来表示该连接对象
*/
private String username;
/**
* 在线人数
*/
private int onlineCount = 0;
public AddOnlineCount(Session session, String username, int onlineCount) {
super();
this.session = session;
this.username = username;
this.onlineCount = onlineCount;
}
synchronized public void run() {
onlineCount++;
WebSocketServer.onlineCount=onlineCount;
log.info(username + "加入!当前在线人数为" + onlineCount);
try {
this.session.getBasicRemote().sendText(username + "进入群聊。在线人数:" + onlineCount);
} catch (IOException e) {
log.error("websocket IO异常");
}
}
}
package com.bm.utils;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bm.service.WebSocketServer;
/**
* @author: zxy
* @data: 2020年3月19日 上午9:00:27
* @description: 将在线人数变量设置为线程安全的,减少在线人数
**/
public class SubOnlineCount extends Thread {
/**
* 日志对象
*/
protected Logger log = LoggerFactory.getLogger(getClass());
/**
* 放入map中的key,用来表示该连接对象
*/
private String username;
/**
* 在线人数
*/
private int onlineCount = 0;
public SubOnlineCount(String username, int onlineCount) {
super();
this.username = username;
this.onlineCount = onlineCount;
}
synchronized public void run() {
onlineCount--;
WebSocketServer.onlineCount=onlineCount;
try {
WebSocketServer server = SpringContextUtil.getBean(WebSocketServer.class);
server.sendInfo(username+" 离开了聊天室。在线人数:"+onlineCount);
} catch (IOException e) {
e.printStackTrace();
}
log.info(username+" 离开了聊天室。在线人数:"+onlineCount);
}
}
package com.bm.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author: zxy
* @data: 2020年3月19日 上午11:13:02
* @description: 获取bean
**/
@Component
public class SpringContextUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextUtil.applicationContext=applicationContext;
}
/**
* 获取ApplicationContext
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过name获取 Bean
* @param name
* @return
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过class获取Bean
* @param clazz
* @return
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过name,以及Clazz返回指定的Bean
* @param name
* @param clazz
* @return
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
4、创建controller
package com.bm.controller;
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.bm.service.WebSocketServer;
/**
* @author: zxy
* @data: 2020年3月18日 下午5:07:24
* @description: 类的描述
**/
@Controller
@RequestMapping("/webSocket")
public class WebSocketController {
@Autowired
WebSocketServer server;
@RequestMapping("/page")
public String page() {
System.out.println("dasda");
return "page/page";
}
@PostMapping("/login")
@ResponseBody
public String login(String username,String password) throws IOException {
server.sendInfo(username + "进入了群聊");
return username;
}
}
5、创建访问页面html,路径:src/main/resources/templates/page/page.html,js就自己准备吧。路径放在:src/main/resources/static下面。没有templates和static文件夹的,自己手动新建出来。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form id="loginForm" >
用户名: <input name="username">
<br>
<input type="button" value="登录" onclick="login()" />
</form>
<textarea id="msg" placeholder="" style="width: 500px;height: 50px" ></textarea>
<input type="button" onclick="send()" value="发送消息" >
<br>
<textarea id="history" style="width: 500px;height: 200px ; max-lines: 10" >
</textarea>
<script src="/modules/jquery/jquery-2.1.1.min.js"></script>
<script type="application/javascript">
var socket ;
//登录过后初始化socket连接
function initSocket(userId) {
if(typeof(WebSocket) == "undefined") {
console.log("您的浏览器不支持WebSocket");
}else {
console.log("您的浏览器支持WebSocket/websocket");
}
//socket连接地址: 注意是ws协议
socket = new WebSocket("ws://192.168.11.112:8085/websocket/"+userId);
socket.onopen = function() {
console.log("Socket 已打开");
};
//获得消息事件
socket.onmessage = function(msg) {
var histroy = $("#history").val();
$("#history").val(histroy+"\r\n"+msg.data);
console.log($(msg));
};
//关闭事件
socket.onclose = function() {
console.log("Socket已关闭");
};
//错误事件
socket.onerror = function() {
alert("Socket发生了错误");
}
$(window).unload(function(){
socket.close();
});
}
//点击按钮发送消息
function send() {
console.log("发送消息: "+$("#msg").val());
socket.send($("#msg").val());
}
//登录
function login() {
$.ajax({
url: "/webSocket/login",
data: $("#loginForm").serialize(),
type: "POST",
success: function (userId) {
if ( userId){
console.log("登录成功!");
initSocket(userId);
}
}
});
}
</script>
</body>
</html>
二、没有spring boot环境的,搭建环境
1、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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com</groupId>
<artifactId>bmSystem</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<!-- Spingboot相关jar包版本 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
</parent>
<!-- 相关jar包 -->
<dependencies>
<!-- Springboot核心jar包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- web开发包:包含Tomcat和Springmvc -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Junit测试jar包 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- spring-boot热部署 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
</project>
2、application.ym,路径不用说了吧。
#服务器配置
server:
port: 8085
#spring相关配置
spring:
thymeleaf:
prefix: classpath:/templates/
suffix: .html
mode: HTML5
encoding: UTF-8
servlet:
content-type: text/html
cache: false
代码写到这里就可以启动服务了。页面如下: