传统的HTTP协议是被动的,单项的即服务器传递信息给客户端,每次发送信息都需要封装HTTP协议中的Headers等相关信息。实时性消息较高的应用场景瓶颈非常明显如聊天室,无法主动获取客户端发送的信息,每次传输数据都需要发送一个完整的HTTP请求,数据传输效率低。
WebSocket其实就是一套新的协议,可以实现Socket编程效果,初次连接的时候初始化一次,只要连接不关闭便可以实现客户端和服务器的实时数据传递。
WebSocket协议是基于HTTP即浏览器的一套新的编程接口,客户端与服务器的连接通过JavaScript编程,页面关闭后连接便失效了,刷新页面会先关闭当前的连接再建立新的连接。
开发工具:
SpringMVC4.0+(spring-websocket包,支持WebSocket编程)
Tomcat 7.0+ (支持WebSocket协议)
实例使用的是SpringMVC4.15+Tomcat7.0.76+JDK1.8
核心代码:
WebSocketInterceptor 拦截器,每次连接时触发。
WebSocketConfig 配置文件,WebSocket实例化。
SocketHandler 业务逻辑处理。
public class WebSocketInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map map) throws Exception {
ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;这里写代码片
String[] str = serverHttpRequest.getServletRequest().getParameterMap().get("id");
map.put("id", str[0]);
return true;
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
}
}ide
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Map map) throws Exception {
ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
String[] str = serverHttpRequest.getServletRequest().getParameterMap().get("id");
map.put("id", str[0]);
return true;
}
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
}
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/socketHandler").addInterceptors(new WebSocketInterceptor()).setAllowedOrigins("*");
}
//@Bean
public WebSocketHandler myHandler() {
return new SocketHandler();
}
}
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/socketHandler").addInterceptors(new WebSocketInterceptor()).setAllowedOrigins("*");
}
//@Bean
public WebSocketHandler myHandler() {
return new SocketHandler();
}
}
@Service
public class SocketHandler extends TextWebSocketHandler {
//在线用户列表(适用于单用户多点)
private static final Map> users;
static {
users = new HashMap<>();
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("成功建立连接");
Integer userId = getClientId(session);
List list = users.get(userId);
if (null == list){
list = new ArrayList<>();
list.add(session);
users.put(userId, list);
}
else
list.add(session);
}
/***
* 接收客户端信息
*/
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
System.out.println(message.getPayload());
}
/**
* 发送信息给指定用户
* @param userloginid 用户标识
* @param message
* @return
*/
public void sendMessageToUser(Integer userloginid, TextMessage message) {
if (users.get(userloginid) == null)
return;
List list = users.get(userloginid);
for(int i=0;iif (!session.isOpen())
continue;
try {
session.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 广播信息
* @param userloginid 用户标识
* @param message
* @return
*/
public void sendMessageToAllUser(Integer userloginid,TextMessage message) {
Set clientIds = users.keySet();
for (Integer clientId : clientIds) {
if(clientId != userloginid)
sendMessageToUser(clientId,message);
}
}
/**
* 广播信息
* @param message
* @return
*/
public void sendMessageToAllUsers(TextMessage message) {
Set clientIds = users.keySet();
for (Integer clientId : clientIds) {
sendMessageToUser(clientId,message);
}
}
/***
* 关闭连接
* @param id
* @param session
*/
public void remove(int id,WebSocketSession session){
if (users.get(id) == null)
return;
List list = users.get(id);
Optional optional = list.stream().filter(m->m.equals(session)).findFirst();
if(optional.isPresent())
list.remove(optional.get());
if(null == list || list.size()==0)
users.remove(id);
}
/***
* 出错是执行,关闭浏览器界面会执行该方法,并自动关闭session
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
}
remove(getClientId(session),session);
}
/***
* 关闭session时执行
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
if (session.isOpen()) {
session.close();
}
remove(getClientId(session),session);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* 获取用户标识
* @param session
* @return
*/
private Integer getClientId(WebSocketSession session) {
return Integer.parseInt(session.getAttributes().get("id").toString());
}
}
HTML代码:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ page isELIgnored="false"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>WebSockettitle>
head>
<body>
<button>socket连接button>
<script src="${pageContext.request.contextPath}/js/jquery-2.1.1.js">script>
<script type="text/javascript">
$(function() {
$("button").click(function(){
if (typeof WebSocket == 'undefined')
alert("浏览器不支持相关特性,请更换为谷歌,UC浏览器。")
else{
//WebSocket客户端建立,生命周期为当前页面。
var ws = new WebSocket("ws://localhost:8123/websocket/socketHandler?id=4")
ws.onopen = function () {
console.log("onpen");
ws.send("客户端发送消息");
}
//页面关闭便会触发
ws.onclose = function () {
console.log("onclose");
}
ws.onmessage = function (msg) {
console.log(msg.data);
var message = msg.data.split(",");
//浏览器支持Notification,并允许接收通知
if($.notificationHandler.requestPermission())
$.notificationHandler.showNotification('${pageContext.request.contextPath}/images/11.png',message[0],message[1]);
}
}
});
})
script>
body>
html>
本实例中运用到了H5的Notification发送通知,相关代码已封装到JS文件里了,便不多叙述了。
下载地址:http://download.csdn.net/download/lishengko/9998130