SpringBoot整合WebSocket初体验
1. 新建项目:
然后手动引一下fastjson就ok了
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version> </dependency>
2. 项目结构
所有文件就是这么个摆放
3. 文件:
WebSocketConfig.java
package kim.nzxy.websocketdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Author: Xiaoyan
* @Date: 2019/5/27 12:49
*/
@Configuration
public class WebSocketConfig {
/**
* 支持websocket
*/
@Bean
public ServerEndpointExporter createServerEndExporter() {
return new ServerEndpointExporter();
}
/**
* 跨域过滤器
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
}
package kim.nzxy.websocketdemo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Author: Xiaoyan
* @Date: 2019/5/27 12:49
*/
@Configuration
public class WebSocketConfig {
/**
* 支持websocket
*/
@Bean
public ServerEndpointExporter createServerEndExporter() {
return new ServerEndpointExporter();
}
/**
* 跨域过滤器
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", buildConfig());
return new CorsFilter(source);
}
private CorsConfiguration buildConfig() {
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
return corsConfiguration;
}
}
PageController.java
package kim.nzxy.websocketdemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author: Xiaoyan
* @Date: 2019/5/27 15:28
*/
@Controller
public class PageController {
@RequestMapping("customer")
public String customer() {
return "customer";
}
@RequestMapping("official")
public String official() {
return "official";
}
}
package kim.nzxy.websocketdemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @Author: Xiaoyan
* @Date: 2019/5/27 15:28
*/
@Controller
public class PageController {
@RequestMapping("customer")
public String customer() {
return "customer";
}
@RequestMapping("official")
public String official() {
return "official";
}
}
Message.java
package kim.nzxy.websocketdemo.entity;
import lombok.Data;
/**
* 这里的Message类并不切合应用,仅仅是为了演示发送一个数据对象
* 但是我想不出来拿什么举例子了,我项目中实际需要的代码需要保密
* 如果是纯文本,则会简单很多,自己去百度WebSocket做一个发消息的东西就好了
*
* @Author: Xiaoyan
* @Date: 2019/5/27 12:53
*/
@Data
public class Message {
private Integer id;
private String theme;
private String content;
private String official;
private String customer;
}
package kim.nzxy.websocketdemo.entity;
import lombok.Data;
/**
* 这里的Message类并不切合应用,仅仅是为了演示发送一个数据对象
* 但是我想不出来拿什么举例子了,我项目中实际需要的代码需要保密
* 如果是纯文本,则会简单很多,自己去百度WebSocket做一个发消息的东西就好了
*
* @Author: Xiaoyan
* @Date: 2019/5/27 12:53
*/
@Data
public class Message {
private Integer id;
private String theme;
private String content;
private String official;
private String customer;
}
MessageDecoder.java
package kim.nzxy.websocketdemo.entity;
import com.alibaba.fastjson.JSON;
import kim.nzxy.websocketdemo.entity.Message;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* json2Company
*
* @author xy
*/
public class MessageDecoder implements Decoder.Text<Message> {
@Override
public void destroy() {
}
@Override
public void init(EndpointConfig arg0) {
}
@Override
public Message decode(String user) {
return JSON.parseObject(user, Message.class);
}
@Override
public boolean willDecode(String arg0) {
return true;
}
}
package kim.nzxy.websocketdemo.entity;
import com.alibaba.fastjson.JSON;
import kim.nzxy.websocketdemo.entity.Message;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* json2Company
*
* @author xy
*/
public class MessageDecoder implements Decoder.Text<Message> {
@Override
public void destroy() {
}
@Override
public void init(EndpointConfig arg0) {
}
@Override
public Message decode(String user) {
return JSON.parseObject(user, Message.class);
}
@Override
public boolean willDecode(String arg0) {
return true;
}
}
MessageEncoder
package kim.nzxy.websocketdemo.entity;
import com.alibaba.fastjson.JSON;
import kim.nzxy.websocketdemo.entity.Message;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* json2Company
*
* @author xy
*/
public class MessageDecoder implements Decoder.Text<Message> {
@Override
public void destroy() {
}
@Override
public void init(EndpointConfig arg0) {
}
@Override
public Message decode(String user) {
return JSON.parseObject(user, Message.class);
}
@Override
public boolean willDecode(String arg0) {
return true;
}
}
package kim.nzxy.websocketdemo.entity;
import com.alibaba.fastjson.JSON;
import kim.nzxy.websocketdemo.entity.Message;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;
/**
* json2Company
*
* @author xy
*/
public class MessageDecoder implements Decoder.Text<Message> {
@Override
public void destroy() {
}
@Override
public void init(EndpointConfig arg0) {
}
@Override
public Message decode(String user) {
return JSON.parseObject(user, Message.class);
}
@Override
public boolean willDecode(String arg0) {
return true;
}
}
TestWebSocket.java
package kim.nzxy.websocketdemo.ws;
import kim.nzxy.websocketdemo.entity.Message;
import kim.nzxy.websocketdemo.entity.MessageDecoder;
import kim.nzxy.websocketdemo.entity.MessageEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author xy
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
* @ServerEndpoint 可以把当前类变成websocket服务类
* id:当前登录对象
* receive:
*/
@ServerEndpoint(value = "/ws/{role}", decoders = MessageDecoder.class, encoders = MessageEncoder.class)
@Component
@Slf4j
public class TestWebSocket {
/**
* 静态变量,用来记录当前客服人员在线连接数。应该把它设计成线程安全的。
*/
private static int official = 0;
/**
* 同上 客户
*/
private static int customer = 0;
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
*/
private static ConcurrentHashMap<String, TestWebSocket> webSocketMapA = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, TestWebSocket> webSocketMapB = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 当前发消息的人员编号
*/
private String userId = "";
/**
* 当前发消息的人对应的开票员
*/
private java.lang.String receiveId = "";
private static synchronized void addA() {
official++;
}
private static synchronized void addB() {
customer++;
}
private static synchronized void subA() {
official--;
}
private static synchronized void subB() {
customer--;
}
private static synchronized int getA() {
return official;
}
private static synchronized int getB() {
return customer;
}
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "role") String role) {
this.session = session;
userId = UUID.randomUUID().toString();
// 在线数加1,a代表客服
if ("a".equals(role)) {
// 客服的id为数字,这样就可以区分客服和用户了,这里假设不会重复
// 这里随机的用户id,所以不方便重连,正式环境大概要和用户表结合
addA();
webSocketMapA.put(userId, this);
} else {
userId = UUID.randomUUID().toString();
addB();
webSocketMapB.put(userId, this);
}
// 这里可以加一个判断,如果webSocketMap.containsKey(id),则系统重新指定一个id
log.info("新连接: " + userId + "----当前客服人数:" + getA() + "----当前客户人数:" + getB());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 从map中删除
if (webSocketMapA.containsKey(userId)) {
webSocketMapA.remove(userId);
// 在线数减1
subA();
log.info("客服下线:" + userId + "----当前客服人数:" + getA() + "----当前客户人数:" + getB());
} else {
webSocketMapB.remove(userId);
subB();
log.info("客户下线:" + userId + "----当前客服人数:" + getA() + "----当前客户人数:" + getB());
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(Message message, @PathParam(value = "role") String role) {
System.out.println(message);
if ("a".equals(role)) {
receiveId = message.getCustomer();
if (message.getCustomer() == null || !webSocketMapB.containsKey(receiveId)) {
webSocketMapA.get(userId).sendMessage("客户已下线");
} else {
webSocketMapB.get(receiveId).sendObj(message);
}
} else {
if (message.getOfficial() != null) {
if (webSocketMapA.containsKey(message.getOfficial())) {
webSocketMapA.get(message.getOfficial()).sendObj(message);
} else {
webSocketMapB.get(userId).sendMessage("当前客服已下线,请换个客服人员重新咨询");
}
} else {
if (webSocketMapA.size() == 0) {
webSocketMapB.get(userId).sendMessage("当前无在线客服");
} else {
// 系统随机指定客服,正式环境应当判断客服接应人数然后再进行指配
int i = new Random().nextInt(webSocketMapA.size());
message.setOfficial(webSocketMapA.keySet().toArray(new String[0])[i]);
message.setCustomer(userId);
webSocketMapA.get(message.getOfficial()).sendObj(message);
}
}
}
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Throwable error) {
log.info(error.toString());
}
/**
* 发送对象到客户端
*/
private void sendObj(Message message) {
try {
this.session.getBasicRemote().sendObject(message);
} catch (EncodeException | IOException e) {
log.info("错误:由用户" + userId + "向" + receiveId + message.toString() + "具体错误为:" + e.toString());
}
}
/**
* 发送文本到客户端
*/
private void sendMessage(String message) {
try {
this.session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.info("错误:由用户" + userId + "向" + receiveId + message + "具体错误为:" + e.toString());
}
}
}
package kim.nzxy.websocketdemo.ws;
import kim.nzxy.websocketdemo.entity.Message;
import kim.nzxy.websocketdemo.entity.MessageDecoder;
import kim.nzxy.websocketdemo.entity.MessageEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author xy
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
* @ServerEndpoint 可以把当前类变成websocket服务类
* id:当前登录对象
* receive:
*/
@ServerEndpoint(value = "/ws/{role}", decoders = MessageDecoder.class, encoders = MessageEncoder.class)
@Component
@Slf4j
public class TestWebSocket {
/**
* 静态变量,用来记录当前客服人员在线连接数。应该把它设计成线程安全的。
*/
private static int official = 0;
/**
* 同上 客户
*/
private static int customer = 0;
/**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
*/
private static ConcurrentHashMap<String, TestWebSocket> webSocketMapA = new ConcurrentHashMap<>();
private static ConcurrentHashMap<String, TestWebSocket> webSocketMapB = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 当前发消息的人员编号
*/
private String userId = "";
/**
* 当前发消息的人对应的开票员
*/
private java.lang.String receiveId = "";
private static synchronized void addA() {
official++;
}
private static synchronized void addB() {
customer++;
}
private static synchronized void subA() {
official--;
}
private static synchronized void subB() {
customer--;
}
private static synchronized int getA() {
return official;
}
private static synchronized int getB() {
return customer;
}
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "role") String role) {
this.session = session;
userId = UUID.randomUUID().toString();
// 在线数加1,a代表客服
if ("a".equals(role)) {
// 客服的id为数字,这样就可以区分客服和用户了,这里假设不会重复
// 这里随机的用户id,所以不方便重连,正式环境大概要和用户表结合
addA();
webSocketMapA.put(userId, this);
} else {
userId = UUID.randomUUID().toString();
addB();
webSocketMapB.put(userId, this);
}
// 这里可以加一个判断,如果webSocketMap.containsKey(id),则系统重新指定一个id
log.info("新连接: " + userId + "----当前客服人数:" + getA() + "----当前客户人数:" + getB());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 从map中删除
if (webSocketMapA.containsKey(userId)) {
webSocketMapA.remove(userId);
// 在线数减1
subA();
log.info("客服下线:" + userId + "----当前客服人数:" + getA() + "----当前客户人数:" + getB());
} else {
webSocketMapB.remove(userId);
subB();
log.info("客户下线:" + userId + "----当前客服人数:" + getA() + "----当前客户人数:" + getB());
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(Message message, @PathParam(value = "role") String role) {
System.out.println(message);
if ("a".equals(role)) {
receiveId = message.getCustomer();
if (message.getCustomer() == null || !webSocketMapB.containsKey(receiveId)) {
webSocketMapA.get(userId).sendMessage("客户已下线");
} else {
webSocketMapB.get(receiveId).sendObj(message);
}
} else {
if (message.getOfficial() != null) {
if (webSocketMapA.containsKey(message.getOfficial())) {
webSocketMapA.get(message.getOfficial()).sendObj(message);
} else {
webSocketMapB.get(userId).sendMessage("当前客服已下线,请换个客服人员重新咨询");
}
} else {
if (webSocketMapA.size() == 0) {
webSocketMapB.get(userId).sendMessage("当前无在线客服");
} else {
// 系统随机指定客服,正式环境应当判断客服接应人数然后再进行指配
int i = new Random().nextInt(webSocketMapA.size());
message.setOfficial(webSocketMapA.keySet().toArray(new String[0])[i]);
message.setCustomer(userId);
webSocketMapA.get(message.getOfficial()).sendObj(message);
}
}
}
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Throwable error) {
log.info(error.toString());
}
/**
* 发送对象到客户端
*/
private void sendObj(Message message) {
try {
this.session.getBasicRemote().sendObject(message);
} catch (EncodeException | IOException e) {
log.info("错误:由用户" + userId + "向" + receiveId + message.toString() + "具体错误为:" + e.toString());
}
}
/**
* 发送文本到客户端
*/
private void sendMessage(String message) {
try {
this.session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.info("错误:由用户" + userId + "向" + receiveId + message + "具体错误为:" + e.toString());
}
}
}
WebSocketApplication.java
package kim.nzxy.websocketdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebsocketDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WebsocketDemoApplication.class, args);
}
}
package kim.nzxy.websocketdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class WebsocketDemoApplication {
public static void main(String[] args) {
SpringApplication.run(WebsocketDemoApplication.class, args);
}
}
4. 前端代码
前端代码颇为简单,而且可以抽取一个js文件,但是由于功能增加后肯定就不方便抽取了,而且为了看代码方便,此处就保留这样
customer.html
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta content="ie=edge" http-equiv="X-UA-Compatible">
<title>客户的页面</title>
</head>
<body>
<div id="hi">
</div>
<input placeholder="主题" type="text">
<input placeholder="内容" type="text">
<button onclick="send()" type="button">发送</button>
</body>
<script>
let websocket = null;
let LData = {};
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost/ws/b");
} else {
alert('当前浏览器不支持\n请更换浏览器');
}
//发送消息
function send() {
let ins = document.getElementsByTagName("input");
LData['theme'] = ins[0].value;
LData['content'] = ins[1].value;
websocket.send(JSON.stringify(LData));
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
let data = event.data;
console.log(data);
if ("{" === data.substr(0, 1)) {
LData = JSON.parse(data);
console.log('LData' + LData);
document.getElementById("hi").innerHTML += LData['theme'] + '----' + LData['content'] + '\n';
} else {
document.getElementById("hi").innerHTML += data + '\n';
}
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("onopen...");
};
//连接关闭的回调方法
websocket.onclose = function () {
console.log("onclose...");
};
//连接发生错误的回调方法
websocket.onerror = function () {
alert("连接服务器失败,请刷新页面重试")
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
};
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta content="ie=edge" http-equiv="X-UA-Compatible">
<title>客户的页面</title>
</head>
<body>
<div id="hi">
</div>
<input placeholder="主题" type="text">
<input placeholder="内容" type="text">
<button onclick="send()" type="button">发送</button>
</body>
<script>
let websocket = null;
let LData = {};
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost/ws/b");
} else {
alert('当前浏览器不支持\n请更换浏览器');
}
//发送消息
function send() {
let ins = document.getElementsByTagName("input");
LData['theme'] = ins[0].value;
LData['content'] = ins[1].value;
websocket.send(JSON.stringify(LData));
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
let data = event.data;
console.log(data);
if ("{" === data.substr(0, 1)) {
LData = JSON.parse(data);
console.log('LData' + LData);
document.getElementById("hi").innerHTML += LData['theme'] + '----' + LData['content'] + '\n';
} else {
document.getElementById("hi").innerHTML += data + '\n';
}
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("onopen...");
};
//连接关闭的回调方法
websocket.onclose = function () {
console.log("onclose...");
};
//连接发生错误的回调方法
websocket.onerror = function () {
alert("连接服务器失败,请刷新页面重试")
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
};
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>
official.html
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta content="ie=edge" http-equiv="X-UA-Compatible">
<title>客服的页面</title>
</head>
<body>
<pre id="hi">
</pre>
<input placeholder="主题" type="text">
<input placeholder="内容" type="text">
<button onclick="send()" type="button">发送</button>
</body>
<script>
let websocket = null;
// 用来保留客户信息用的
let LData = {};
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost/ws/a");
} else {
alert('当前浏览器不支持\n请更换浏览器');
}
//发送消息
function send() {
let ins = document.getElementsByTagName("input");
LData['theme'] = ins[0].value;
LData['content'] = ins[1].value;
console.log(LData);
websocket.send(JSON.stringify(LData));
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
let data = event.data;
console.log("收到:"+data);
if ("{" === data.substr(0, 1)) {
LData = JSON.parse(data);
console.table(LData);
document.getElementById("hi").innerHTML += LData['theme'] + '----' + LData['content'] + '\n';
} else {
document.getElementById("hi").innerHTML += data + '\n';
}
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("onopen...");
};
//连接关闭的回调方法
websocket.onclose = function () {
console.log("onclose...");
};
//连接发生错误的回调方法
websocket.onerror = function () {
alert("连接服务器失败,请刷新页面重试")
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
};
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>
<!doctype html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
name="viewport">
<meta content="ie=edge" http-equiv="X-UA-Compatible">
<title>客服的页面</title>
</head>
<body>
<pre id="hi">
</pre>
<input placeholder="主题" type="text">
<input placeholder="内容" type="text">
<button onclick="send()" type="button">发送</button>
</body>
<script>
let websocket = null;
// 用来保留客户信息用的
let LData = {};
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost/ws/a");
} else {
alert('当前浏览器不支持\n请更换浏览器');
}
//发送消息
function send() {
let ins = document.getElementsByTagName("input");
LData['theme'] = ins[0].value;
LData['content'] = ins[1].value;
console.log(LData);
websocket.send(JSON.stringify(LData));
}
//接收到消息的回调方法
websocket.onmessage = function (event) {
let data = event.data;
console.log("收到:"+data);
if ("{" === data.substr(0, 1)) {
LData = JSON.parse(data);
console.table(LData);
document.getElementById("hi").innerHTML += LData['theme'] + '----' + LData['content'] + '\n';
} else {
document.getElementById("hi").innerHTML += data + '\n';
}
};
//连接成功建立的回调方法
websocket.onopen = function () {
console.log("onopen...");
};
//连接关闭的回调方法
websocket.onclose = function () {
console.log("onclose...");
};
//连接发生错误的回调方法
websocket.onerror = function () {
alert("连接服务器失败,请刷新页面重试")
};
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function () {
closeWebSocket();
};
//关闭WebSocket连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>