github-demo(idea):https://github.com/bbwangbb/csnd-WebSocket-demo
1.原生使用(Maven)
1)导入jar包,javax.websocket-api,如果使用jar导入tomcat/lib下的tomcat-websocket.jar和websocket-api.jar
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
<scope>provided</scope>
</dependency>
2)配置类
package cn.mb.websocket;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.CopyOnWriteArraySet;
//该注解用来指定连接的 url,类似请求的url -> ws://主机:端口/websocket 即为连接地址
@ServerEndpoint("/websocket")
public class WebsocketConfig {
//可以通过此来记录在线人数
public static int onlineCount = 0;
//可以用通过静态集合的方式存储session对象,在别处可以直接调用来逐个发送消息
public static CopyOnWriteArraySet<Session> sessions = new CopyOnWriteArraySet<>();
public WebsocketConfig() {
System.out.println("init...");
}
@OnOpen//连接打开时调用
public void open(Session session) {
sessions.add(session);
System.out.println("open...");
}
@OnClose//连接关闭时调用
public void close(Session session) {
sessions.remove(session);
System.out.println("close...");
}
@OnMessage//客户端发送消息时调用
public void send(String message, Session session) {
System.out.println("客户端信息:" + message);
System.out.println("send...");
//给用户发送信息
}
@OnError//发生错误时调用
public void error(Session session, Throwable error) {
System.out.println("error...");
}
}
3)前端页面
<!DOCTYPE html>
<html>
<head>
<title>My WebSocket</title>
<script src="jquery-2.1.0.js"></script>
</head>
<body>
<button id="open">Open</button>
<br/>
<input type="text" id="text">
<button id="send">Send</button>
<br/>
<button id="close">Close</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
$(function () {
var websocket = null;
//开起连接
$("#open").click(function () {
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/websocket");
} else {
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function (event) {
$("#message").html("Connect error!")
};
//连接成功建立的回调方法
websocket.onopen = function (event) {
$("#message").html("Connect successfully!");
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
$("#message").html("服务端消息:" + event.data);
}
})
//关闭连接
$("#close").click(function () {
websocket.close();
$("#message").html("Connect closing!")
})
//发送消息
$("#send").click(function () {
var message = $("#text").val();
websocket.send(message);
$("#message").html("Send successfully!");
})
})
</script>
</html>
2.Spring使用
1)导入jar
<!--Spring Boot 的websocket方式-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!--Spring集成-->
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-messaging</artifactId>-->
<!-- <version>5.0.9.RELEASE</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-websocket</artifactId>-->
<!-- <version>5.0.9.RELEASE</version>-->
<!-- </dependency>-->
<!--客户端使用的是sockjs方式,相当于导入前端需要使用的js文件-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.0</version>
</dependency>
2)配置类
package cn.mb.websocket;
import cn.mb.interceptor.HttpHandShakeInterceptor;
import cn.mb.listener.STOMPConnectEventListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker//启用websocket注解
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override//配置消息代理
public void configureMessageBroker(MessageBrokerRegistry config) {
/**
* 与 @MessageMapping 中的url拼接后就是 客户端可以发送数据的 url
* @MessageMapping("/testWebSocket")
* 客户端即可向 /app/testWebSocket 发送数据
*/
config.setApplicationDestinationPrefixes("/app");
//前端可以订阅 /topic/... 的url
config.enableSimpleBroker("/topic");
}
@Override//注册 Stomp 端点
public void registerStompEndpoints(StompEndpointRegistry registry) {
//添加一个/spring-websocket端点,客户端就可以通过这个端点来进行连接;
registry.addEndpoint("/spring-websocket")
.addInterceptors(new HttpHandShakeInterceptor())//设置握手拦截器
.setAllowedOrigins("*") // 添加允许跨域访问
.withSockJS();//添加SockJS支持
}
@Bean//注入监听器
public STOMPConnectEventListener STOMPConnectEventListener(){
return new STOMPConnectEventListener();
}
}
3)监听器(可选)
package cn.mb.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.web.socket.messaging.SessionConnectEvent;
public class STOMPConnectEventListener implements ApplicationListener<SessionConnectEvent> {
@Override
public void onApplicationEvent(SessionConnectEvent sessionConnectEvent) {
//打开链接时会触发此方法
}
}
4)拦截器(可选)
package cn.mb.interceptor;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import java.util.Map;
//声明后需要在配置类中配置
public class HttpHandShakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
System.out.println("beforeHandshake...");
return true;//返回false会疯狂请求握手
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
System.out.println("afterHandshake...");
}
}
5)控制层
package cn.mb.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
@Controller
public class WebSocketController {
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;
@MessageMapping("/testWebSocket")
@SendTo("/topic/destination")
public String testWebSocket(String message) throws Exception {
/**
* 单播(私聊):
* 1.两人订阅相同的 url
* 2.这样发信息的时候可以互相看到,数据库中可以在存储好友的信息的时候带上一个url(个人想法)
*/
simpMessagingTemplate.convertAndSend("/topic/bb", "我是bb,你在吗?");
System.out.println("客户端说:" + message);
return "Hi, client!";
}
}
6)前端页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="webjars/jquery/3.1.0/jquery.min.js"></script>
<script src="webjars/sockjs-client/1.0.2/sockjs.min.js"></script>
<script src="webjars/stomp-websocket/2.3.3/stomp.min.js"></script>
</head>
<body>
<button id="open">Open</button>
<br/>
<button id="send">Send</button>
<br/>
<button id="close">Close</button>
<div id="message">
</div>
<script>
$(function () {
var socket = null;
var stompClient = null;
//开起连接
$("#open").click(function () {
//创建 websocket对象
socket = new SockJS('/spring-websocket');//固定
stompClient = Stomp.over(socket);
//连接成功的回调函数
stompClient.connect({username : 'bb'}, function (frame) {
//...
//打印连接状态
console.log('Connected: ' + frame);
$("#message").html("连接成功!")
/*
3.连接成功后订阅目标地址
client.subscribe(destination, callback)
destination:目标地址
callback:有人向这个地址发送广播该回调函数就会执行
*/
stompClient.subscribe('/topic/bb', function (resp) {//DB读取
$("#message").html("服务端回复:" + resp.body)
});
//可以订阅多个,每个写一次方法即可
// stompClient.subscribe('/topic/cc', function (resp) {//DB读取
// $("#message").html("服务端回复:" + resp.body)
// });
});
})
//关闭连接
$("#close").click(function () {
if (stompClient !== null) {
stompClient.disconnect();
$("#message").html("连接关闭!")
}
})
//发送消息
$("#send").click(function () {
/*
client.send(destination, {}, body);
destination:目标地址
{}:请求头
body:请求信息(JSON格式)
*/
stompClient.send("/app/testWebSocket", {}, JSON.stringify({message : 'Hi, server!'}));
})
})
</script>
</body>
</html>
注意:在Struts中使用要放行该url,在struts.xml中配置以下内容
<constant name="struts.action.excludePattern" value="/你的url"></constant>
个人理解:
1.连接WebSocket服务端
2.连接成功后,客户端与服务端之间可以相互发送信息
3.至于怎么做单播,原生可以使用存储Session方式,Spring中可以使用SimpMessagingTemplate对象发送(订阅同一个url)
理论后期弥补。