聊天室项目
<!--websocket-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
利用pageCode,简单的模拟用户token
//value 映射地址,encoders 编码器,对传递的参数进行处理
@ServerEndpoint(value = "/websocket/{pageCode}",encoders = { ResultEncoder.class})
@Component
public class WebSocket {
private static final String loggerName=WebSocket.class.getName();
//concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
public static Map<String, Session> electricSocketMap = new ConcurrentHashMap<String,Session>();
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(@PathParam("pageCode") String pageCode, Session session) {
electricSocketMap.put(pageCode,session);
sendMsg(session,new MessageResult(MessageResult.SUCCESS,electricSocketMap.size()+"",""));
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(@PathParam("pageCode") String pageCode,Session session) {
if (electricSocketMap.containsKey(pageCode)){
electricSocketMap.remove(pageCode);
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("websocket received message:"+message);
String[] split = message.split("--");
if(split[1].contains("0")){
//发送消息给所有人
sendAll(new MessageResult(MessageResult.SUCCESS,electricSocketMap.size()+"",message));
}else {
//发送消息给指定用户
sendMsg(electricSocketMap.get(split[1]), new MessageResult(MessageResult.SUCCESS, electricSocketMap.size() + "", message));
}
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
*
* @param session
* @param message 消息结果集
*/
public static void sendMsg(Session session, Object message) {
try {
session.getBasicRemote().sendObject(message);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 群发消息
* @param message 消息内容
*/
private void sendAll(Object message) {
for (Map.Entry<String, Session> sessionEntry : electricSocketMap.entrySet()) {
sessionEntry.getValue().getAsyncRemote().sendObject(message);
}
}
}
@Component
public class WebsocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
结果集
public class MessageResult {
public static final int FAILURE = 0;//失败
public static final int SUCCESS = 1;//成功
public static final int REQUIRED = 2;//必要参数为空
private int key;
private String message;
private Object data;
public MessageResult(int key, String message, Object data) {
this.key = key;
this.message = message;
this.data = data;
}
public MessageResult(int key, String message) {
this.key = key;
this.message = message;
}
public MessageResult() {
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getKey() {
return key;
}
public void setKey(int key) {
this.key = key;
}
}
编码器
public class ResultEncoder implements Encoder.Text<MessageResult> {
@Override
public void destroy() {
// TODO Auto-generated method stub
}
@Override
public void init(EndpointConfig arg0) {
// TODO Auto-generated method stub
}
@Override
public String encode(MessageResult message) throws EncodeException {
JSONObject jsonObject = JSONObject.fromObject(message);
return jsonObject.toString();
}
}
<html>
<head>
<meta charset="UTF-8">meta>
<title>springboot项目WebSocket测试demotitle>
head>
<body>
<h3>springboot项目websocket测试demoh3>
<h4>测试说明h4>
<h5>文本框中数据数据,点击‘发送测试’,文本框中的数据会发送到后台websocket,后台接受到之后,会再推送数据到前端,展示在下方;点击关闭连接,可以关闭该websocket;可以跟踪代码,了解具体的流程;代码上有详细注解h5>
<br />
<h5>在线人数:<span id="number">span>h5>
<br />
<input id="text" type="text" />
<button onclick="send()">发送消息button>--[*] 给某个用户发送消息 --0给所有人发送消息
<hr />
<button onclick="clos()">关闭连接button>
<hr />
<div id="message">div>
<script>
var url=window.location.href;
var websocket = null;
if('WebSocket' in window){
websocket = new WebSocket("ws://127.0.0.1:8080/websocket/"+getUrlParams('id'));
}else{
alert("您的浏览器不支持websocket");
}
websocket.onerror = function(){
setMessageInHtml("send error!");
}
websocket.onopen = function(){
setMessageInHtml("connection success!welcome")
}
websocket.onmessage = function(event){
console.log(event)
var parse = JSON.parse(event.data);
if(parse.data!=""){
setMessageInHtml(parse.data.split("--")[0]);
setMessageInHtmlCount(parse.message);
}else{
setMessageInHtmlCount(parse.message);
}
}
websocket.onclose = function(){
setMessageInHtml("closed websocket!")
}
window.onbeforeunload = function(){
clos();
}
function setMessageInHtml(message){
document.getElementById('message').innerHTML += ""+message;
}
function setMessageInHtmlCount(message){
document.getElementById('number').innerHTML = message;
}
function clos(){
websocket.close(3000,"强制关闭");
}
function send(){
var msg = document.getElementById('text').value;
websocket.send(msg);
}
function getUrlParams(name) { // 不传name返回所有值,否则返回对应值
var url = window.location.search;
if (url.indexOf('?') == 1) { return false; }
url = url.substr(1);
url = url.split('&');
var name = name || '';
var nameres;
// 获取全部参数及其值
for(var i=0;i<url.length;i++) {
var info = url[i].split('=');
var obj = {};
obj[info[0]] = decodeURI(info[1]);
url[i] = obj;
}
// 如果传入一个参数名称,就匹配其值
if (name) {
for(var i=0;i<url.length;i++) {
for (const key in url[i]) {
if (key == name) {
nameres = url[i][key];
}
}
}
} else {
nameres = url;
}
// 返回结果
return nameres;
}
script>
body>
html>
前端定时发送一个socket消息给后端,前端如果接受不到消息后,重建连接。
//创建websocket连接
function createWebSocket() {
try {
websocket = new WebSocket(wsUrl);
init();
} catch(e) {
reconnect(wsUrl);
}
}
//websocket的方法
function init() {
websocket.onclose = function () {
reconnect(wsUrl);
};
websocket.onerror = function() {
reconnect(wsUrl);
};
websocket.onopen = function () {
//心跳检测重置
heartCheck.start();
};
websocket.onmessage = function (event) {
//拿到任何消息都说明当前连接是正常的
var parse = JSON.parse(event.data);
if(parse.data!=""&&parse.data!=null){
setMessageInHtml(parse.data);
setMessageInHtmlCount(parse.message);
}else if(parse.message.indexOf("heartbeat")!=-1){
console.log(parse.message)
}else{
setMessageInHtmlCount(parse.message);
}
//重新设置心跳连接
heartCheck.reset();
}
}
//重连
function reconnect(url) {
if(lockReconnect) {
return;
};
lockReconnect = true;
//没连接上会一直重连,设置延迟避免请求过多
tt && clearTimeout(tt);
tt = setTimeout(function () {
//重连
createWebSocket(url);
lockReconnect = false;
}, 4000);
}
//心跳检测
var heartCheck = {
timeout: 3000,
timeoutObj: null,
serverTimeoutObj: null,
start: function(){
console.log('start');
var self = this;
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(function(){
//这里发送一个心跳,后端收到后,返回一个心跳消息,
console.log('55555');
websocket.send("heartbeat reconnect~~~"+getUrlParams('id'));
}, this.timeout)
},
reset: function(){
clearTimeout(this.timeoutObj);
this.start();
},
}
createWebSocket(wsUrl);
/*
* 工具方法
* */
function setMessageInHtml(message){
document.getElementById('message').innerHTML += ""+message;
}
function setMessageInHtmlCount(message){
document.getElementById('number').innerHTML = message;
}
function clos(){
websocket.close(3000,"强制关闭");
}
function send(){
var msg = document.getElementById('text').value;
websocket.send(msg);
}
function getUrlParams(name) { // 不传name返回所有值,否则返回对应值
var url = window.location.search;
if (url.indexOf('?') == 1) { return false; }
url = url.substr(1);
url = url.split('&');
var name = name || '';
var nameres;
// 获取全部参数及其值
for(var i=0;i<url.length;i++) {
var info = url[i].split('=');
var obj = {};
obj[info[0]] = decodeURI(info[1]);
url[i] = obj;
}
// 如果传入一个参数名称,就匹配其值
if (name) {
for(var i=0;i<url.length;i++) {
for (const key in url[i]) {
if (key == name) {
nameres = url[i][key];
}
}
}
} else {
nameres = url;
}
// 返回结果
return nameres;
}
end
websocket集群方案