根据业务需求需要服务端主动发送消息到客户端,考虑到简单上手就选择了webSocket,下面就开始了采坑记录
- 既然是整合使用webSocket 那么首先先创建springboot 项目
为了便捷我采用的是 IntelliJ IDEA 创建springboot 项目
idea左上角 选择 File ----New --- Project -----。。。不在细说创建springboot项目
2. 创建好项目后 测试项目是否能正常访问 :在项目中添加
找到XXXApplicatin 文件 ,加上 @Restcontroller 标签 ,然后接口访问:如下
@SpringBootApplication
@RestController
public class WebtestApplication {
public static void main(String[] args) {
SpringApplication.run(WebtestApplication.class, args);
}
@RequestMapping("test")
private String test(){
return "ok";
}
}
一切ok,下面正常访问:
现在可以确认springboot 项目创建成功了,下面就开始引入webSocket 依赖
3.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.5.4.RELEASE</version>
</dependency>
配置启动文件
@Configuration
public class webSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
创建服务端
@Component
@ServerEndpoint("/websocket")
public class WebSocketServer {
//静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
//与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("新连接加入!" + onlineCount++);
try {
sendMessage("连接成功");
} catch (IOException e) {
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //从set中删除
subOnlineCount(); //在线数减1
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
//群发消息
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 群发自定义消息
* */
public static void sendInfo(String message) throws IOException {
for (WebSocketServer item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
continue;
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocketServer.onlineCount++;
System.out.println("新连接进来"+WebSocketServer.onlineCount++);
}
public static synchronized void subOnlineCount() {
WebSocketServer.onlineCount--;
}
}
下面创建客户端:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>WebSocket Test</title>
<script type="text/javascript">
var websocket = null;
var url = "ws://192.168.9.141:8080/websocket";
//判断当前浏览器是否支持WebSocket
if ("WebSocket" in window){
//打开一个WebSocket连接
websocket = new WebSocket(url);
} else {
alert("您的浏览器不支持WebSocket!");
}
//连接成功建立的回调方法
websocket.onopen = function(event){
setMessage("WebSocket连接成功!");
//websocket.send("Hello WebSocket!");
}
//接收到消息的回调方法
websocket.onmessage = function (event){
if(typeof event.data == 'string'){
var msg = event.data;
if(msg.endsWith(".png") || msg.endsWith(".jpeg")){//图片url
var img = document.createElement("img");
img.src = msg;
document.getElementById("message").appendChild(img);
} else if(msg.endsWith(".mp3") || msg.endsWith(".wav") || msg.endsWith(".ogg")){//语音url
var audio = document.createElement("audio");
audio.src = msg;
audio.controls = true;
document.getElementById("message").appendChild(audio);
} else {//文本消息
setMessage(msg);
}
}
}
//关闭WebSocket
websocket.onclose = function(event){
setMessage("WebSocket连接关闭!");
}
//连接发生错误的回调方法
websocket.onerror = function(event){
setMessage("连接发生了错误!");
}
//发送文本消息到服务器
function sendMsg(){
if (websocket.readyState == 1){ //0-CONNECTING;1-OPEN;2-CLOSING;3-CLOSED
var msg = document.getElementById('text').value;
if(msg) websocket.send(msg);
document.getElementById("text").value= "";
document.getElementById('text').focus();
} else {
alert("websocket connection closed!");
}
}
//发送图片或音频文件到服务器
function sendFile(){
if (websocket.readyState == 1){ //0-CONNECTING;1-OPEN;2-CLOSING;3-CLOSED
var inputElement = document.getElementById("file");
var fileList = inputElement.files;
for ( var i = 0; i < fileList.length; i++) {
//发送文件名
//websocket.send(fileList[i].name);
var reader = new FileReader();
//以二进制形式读取文件
reader.readAsArrayBuffer(fileList[i]);
//文件读取完毕后该函数响应
reader.onload = function loaded(event) {
var binaryString = event.target.result;
//发送二进制字符串数组
websocket.send(binaryString);
}
}
} else {
alert("websocket connection closed!");
}
}
//将消息显示在页面上
function setMessage(msg){
document.getElementById('message').innerHTML += msg + '<br/>';
}
//清空页面上的消息
function clearMsg(){
document.getElementById('message').innerHTML = "";
}
//关闭连接
function closeWebSocket(){
if(websocket != null) websocket.close();
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常
window.onbeforeunload = function(){
closeWebSocket();
}
</script>
</head>
<body>
<h3>WebSocket Demo 1</h3>
<input id="text" type="text"/>
<button onclick="sendMsg()">发送消息</button>
<button onclick="clearMsg()">清空消息</button>
<button onclick="closeWebSocket()">关闭 WebSocket</button><br/>
<input id="file" type="file" multiple />
<button onclick="sendFile()">发送图片或语音</button>
<div id="message"></div>
</body>
</html>
好了现在以为大功告成,开开心心的启动项目(实际上开始采坑)
卧槽启动报错:Failed to register @ServerEndpoint class: class com.example.webtest.webSocket.WebSocketServer
这是说注入@ServerEndpoint 标签失败,一头雾水啊,就在网上求助度娘
看了几篇文章大概有个浅显的认知了,说是自身容器的问题,就是在Tomcat 中启动webSocket 不需要配置
@Configuration
public class webSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
注释掉后,果然能正常运行了,美滋滋。。。
打开客户端一直连接不上报错:404 自己反复的确认项目访问路径,可是就是连不上服务端
在一通求助度娘,半小时后看了几篇文章似乎有点眉目了
原来连接不到webSocket服务端还是和 启动webSocket的容器有关,主要是容器里没有启动webSocket的服务端
参考于:https://www.cnblogs.com/bianzy/p/5822426.html
核心是@ServerEndpoint这个注解。这个注解是Javaee标准里的注解,tomcat7以上已经对其进行了实现,如果是用传统方法使用tomcat发布项目,只要在pom文件中引入javaee标准即可使用。
然后在pom 文件里加上:
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
发现还是连接不上,客户端报错 404
于是想到打开配置文件:
然后重新启动项目,再去打开客户端连接服务端,
终于看到连接成功,
总结出现的这一切bug,归根结底还是自己对springboot 不够熟悉,下面继续加强对springboot的学习