spring boot与websocket的双工异步通信--消息
2019-08-11 本文已影响0人
KyGb
HTTP与websocket协议区别
HTTP协议的特点
- [ ] 1.无状态协议
- [ ] 2.用于通过Internet发送请求消息和响应消息
- [ ] 3.使用端口接收和发送消息,默认端口80
缺点:无法直接实现双向通信与消息推送
WebSocket:
是HTML5开始提供的一种浏览器与服务器间进行全双工通讯的网络技术。依靠这种技术可以实现客户端和服务器端的长连接,双向实时通信。
特点:
- [ ] 1.事件驱动
- [ ] 2.异步
- [ ] 3.使用ws或者wss协议的客户端socket
缺点:
少部分浏览器不支持,浏览器支持的程度与方式有区别。
1.应用控制层:
@SpringBootApplication
@RestController
public class WebSocketChatApplication {
/**
* 登陆界面
*/
@GetMapping("/")
public ModelAndView login() {
return new ModelAndView("/login");
}
/**WebSecurityConfigurerAdapter
* 聊天界面
*/
@GetMapping("/index")
public ModelAndView index(String username, String password, HttpServletRequest request) throws UnknownHostException {
if (StringUtils.isEmpty(username)) {
username = "匿名用户";
}
ModelAndView mav = new ModelAndView("/chat");
mav.addObject("username", username);
// webSocketUrl -> ws://ip地址:8080/chat
mav.addObject("webSocketUrl", "ws://"+InetAddress.getLocalHost().getHostAddress()+":"+request.getServerPort()+request.getContextPath()+"/chat");
return mav;
}
public static void main(String[] args) {
SpringApplication.run(WebSocketChatApplication.class, args);
}
}
2.WebSocket服务端
同步方式阻塞式:推送消息给每个会话
/**
* WebSocket 聊天服务端
*
* ServerEndpoint WebSocket服务端 需指定端点的访问路径
* Session WebSocket会话对象 通过它给客户端发送消息
*/
@Component
@ServerEndpoint("/chat")
public class WebSocketChatServer {
/**
* 全部在线会话 PS: 基于场景考虑 这里使用线程安全的Map存储会话对象。
*/
private static Map<String, Session> onlineSessions = new ConcurrentHashMap<>();
/**
* 当客户端打开连接:1.添加会话对象 2.更新在线人数
*/
@OnOpen
public void onOpen(Session session) {
onlineSessions.put(session.getId(), session);
sendMessageToAll(Message.jsonStr(Message.ENTER, "", "", onlineSessions.size()));
}
/**
* 当客户端发送消息:1.获取它的用户名和消息 2.发送消息给所有人
* <p>
* PS: 这里约定传递的消息为JSON字符串 方便传递更多参数!
*/
@OnMessage
public void onMessage(Session session, String jsonStr) {
Message message = JSON.parseObject(jsonStr, Message.class);
sendMessageToAll(Message.jsonStr(Message.SPEAK, message.getUsername(), message.getMsg(), onlineSessions.size()));
}
/**
* 当关闭连接:1.移除会话对象 2.更新在线人数
*/
@OnClose
public void onClose(Session session) {
onlineSessions.remove(session.getId());
sendMessageToAll(Message.jsonStr(Message.QUIT, "", "", onlineSessions.size()));
}
/**
* 当通信发生异常:打印错误日志
*/
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
/**
* 公共方法:发送信息给所有人
*/
private static void sendMessageToAll(String msg) {
onlineSessions.forEach((id, session) -> {
try {
session.getBasicRemote().sendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
});
}
}
3.WebSocket配置
扫描和注册所有携带ServerEndPoint注解的实例。
@Configuration
public class WebSocketConfig {
/**
* 用于扫描和注册所有携带ServerEndPoint注解的实例。
* <p>
* PS:若部署到外部容器 则无需提供此类。
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
public class Message {
public static final String ENTER = "ENTER";
public static final String SPEAK = "SPEAK";
public static final String QUIT = "QUIT";
private String type;//消息类型
private String username; //发送人
private String msg; //发送消息
private int onlineCount; //在线用户数
}
客户端:
<script th:inline="javascript">
/**
* WebSocket客户端
*
* 使用说明:
* 1、WebSocket客户端通过回调函数来接收服务端消息。例如:webSocket.onmessage
* 2、WebSocket客户端通过send方法来发送消息给服务端。例如:webSocket.send();
*/
function getWebSocket() {
/**
* WebSocket客户端 PS:URL开头表示WebSocket协议 中间是域名端口 结尾是服务端映射地址
*/
var webSocket = new WebSocket(/*[[${webSocketUrl}]]*/ 'ws://localhost:8080/chat');
/**
* 当服务端打开连接
*/
webSocket.onopen = function (event) {
console.log('WebSocket打开连接');
};
/**
* 当服务端发来消息:1.广播消息 2.更新在线人数
*/
webSocket.onmessage = function (event) {
console.log('WebSocket收到消息:%c' + event.data, 'color:green');
//获取服务端消息
var message = JSON.parse(event.data) || {};
var $messageContainer = $('.message-container');
if (message.type === 'SPEAK') {
$messageContainer.append(
'<div class="mdui-card" style="margin: 10px 0;">' +
'<div class="mdui-card-primary">' +
'<div class="mdui-card-content message-content">' + message.username + ":" + message.msg + '</div>' +
'</div></div>');
}
$('.chat-num').text(message.onlineCount);
//防止刷屏
var $cards = $messageContainer.children('.mdui-card:visible').toArray();
if ($cards.length > 5) {
$cards.forEach(function (item, index) {
index < $cards.length - 5 && $(item).slideUp('fast');
});
}
};
/**
* 关闭连接
*/
webSocket.onclose = function (event) {
console.log('WebSocket关闭连接');
};
/**
* 通信失败
*/
webSocket.onerror = function (event) {
console.log('WebSocket发生异常');
};
return webSocket;
}
var webSocket = getWebSocket();
/**
* 通过WebSocket对象发送消息给服务端
*/
function sendMsgToServer() {
var $message = $('#msg');
if ($message.val()) {
webSocket.send(JSON.stringify({username: $('#username').text(), msg: $message.val()}));
$message.val(null);
}
}
/**
* 清屏
*/
function clearMsg() {
$(".message-container").empty();
}
/**
* 使用ENTER发送消息
*/
document.onkeydown = function (event) {
var e = event || window.event || arguments.callee.caller.arguments[0];
e.keyCode === 13 && sendMsgToServer();
};
</script>
admin给user发送信息
user收到信息并回复