websocket
2017-12-21 16:27
106 查看
1 Jar包准备和配置
1.1 所需jar包(pom文件引入)
Spring基础jar包:即web项目所需的spring jar包;
Websocket jar包:javax.websocket-ap(注意scope必须为provided,否则runtime会冲突)和spring-websocket。如图:
1.2 所需xml配置如图
注1:path为前台websocket请求路径。
注2:websocket:handshake-interceptors为websocket握手建立连接前自定义拦截器。
注3:WSHandler 为连接建立后的逻辑处理,连接建立后数据传输出错的处理,前台有消息相互传递的逻辑处理,连接关闭后的逻辑处理。
2 设计逻辑
1:jsp建立websocket对象和请求路径
2:握手前获取学校id参数并放入WebSocketSession中
3:WebSocketSession链接成功,把当前WebSocketSession放入集合中
4:后台推送消息——遍历WebSocketSession集合对应学校id的消息json字符串推送到前端页面。
5:消息传输错误——关闭当前WebSocketSession链接,并把它从WebSocketSession集合中移除,重新建立WebSocketSession链接
6:前端页面关闭——关闭当前WebSocketSession链接,并把它从WebSocketSession集合中移除
3 实现代码
3.1 自定义拦截器代码
package com.sunsharing.ihome.air.web.websocket;
import java.util.Map;
import org.apache.log4j.Logger;
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;
/**
* websocket拦截器类
* <p>
* </p>
*
* @author chuhaitao 2017年10月25日 下午2:38:51
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2017年10月25日
* @modify by reason:{方法名}:{原因}
*/
public class WSHandshakeInterceptor implements HandshakeInterceptor {
Logger logger = Logger.getLogger(WSHandshakeInterceptor.class);
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse,
WebSocketHandler webSocketHandler, Exception ex) {
// TODO Auto-generated method stub
}
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse,
WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
// 获取websocket请求地址
String url = serverHttpRequest.getURI().toString();
if (url.indexOf("schoolId") == -1) {// 判断是否有学校id参数
return true;
}
// 获取学校id
String schoolId = url.substring(url.indexOf("=") + 1);
logger.info("获取到学校id:schoolId=" + schoolId);
map.put("schoolId", schoolId);
return true;
}
}
3.2 Websocket处理类代码
package com.sunsharing.ihome.air.web.websocket;
import java.io.IOException;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
* websocekt类
* <p>
* </p>
*
* @author chuhaitao 2017年10月23日 上午11:12:48
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2017年10月23日
* @modify by reason:{方法名}:{原因}
*/
public class WSHandler implements WebSocketHandler {
Logger logger = Logger.getLogger(WebSocketHandler.class);
@Autowired
private WSClientManager wsClientManager;
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
wsClientManager.getWebSocketSessions().add(session);
logger.info("链接websocket成功");
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
// 获取消息
String[] messages = message.getPayload().toString().split("&");
// 获取学校id
String schoolId = "大cha";
// 获取要推送的消息
String messageValue = messages[0];
if (messages.length > 1) {
schoolId = messages[0];
messageValue = messages[1];
}
final TextMessage returnMessage = new TextMessage(messageValue);
try {
// 获取所有客户端
Iterator<WebSocketSession> it = wsClientManager.getWebSocketSessions().iterator();
while (it.hasNext()) {
WebSocketSession client = it.next();
if (session != client) {// 判断是否为当前发送消息的客户端
if (client.isOpen()) {
if (schoolId.equals(client.getAttributes().get("schoolId"))) {// 判断是否是同一学校的客户端
client.sendMessage(returnMessage);
}
}
}
}
} catch (IOException e) {
logger.error("推送消息错误", e);
} catch (NullPointerException e) {
logger.error("无客户端,请连接客户端");
} catch (Exception e) {
logger.error("发送错误");
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
wsClientManager.getWebSocketSessions().remove(session);
}
logger.error("发生错误链接关闭,错误信息为:" + exception.getMessage());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
wsClientManager.getWebSocketSessions().remove(session);
logger.info("websocket链接关闭");
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
3.3 后端消息推送代码
/**
* 推送消息给指定用户
*
* @author chuhaitao 2017年10月25日 下午2:10:38
* @param schoolId
* @param message
*/
public void sendMessageToUser(String schoolId, String message) {
try {
final Iterator<WebSocketSession> it = wsClientManager.getWebSocketSessions().iterator();
while (it.hasNext()) {
final WebSocketSession client = it.next();
if (client.isOpen()) {
if (schoolId.equals(client.getAttributes().get("schoolId"))) {
final TextMessage textMessage = new TextMessage(message);
client.sendMessage(textMessage);
}
}
}
} catch (final IOException e) {
logger.error("推送消息错误", e);
} catch (final NullPointerException e) {
logger.error("无客户端,请连接客户端");
} catch (final Exception e) {
logger.error("发送错误");
}
}
3.4 WebSocketSession集合代码
package com.sunsharing.ihome.air.web.websocket;
import java.util.HashSet;
import java.util.Set;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
/**
* 发送客户端类
* <p>
* </p>
*
* @author chuhaitao 2017年10月23日 上午11:10:43
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2017年10月23日
* @modify by reason:{方法名}:{原因}
*/
@Component
public class WSClientManager {
private final Set<WebSocketSession> webSocketSessions = new HashSet<WebSocketSession>();
public Set<WebSocketSession> getWebSocketSessions() {
return webSocketSessions;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Web Socket JavaScript Echo Client</title>
<script language="javascript" type="text/javascript">
var echo_websocket;
var url = ((window.location.protocol == "https:") ? "wss:" : "ws:")
+ "//" + window.location.host
+ "/springweb/websocket.do";
function createWebsocket()
{
echo_websocket = new WebSocket(url);
echo_websocket.onopen = function (evt) {
writeToScreen("Connected !");
//doSend(textID.value);
};
echo_websocket.onmessage = function (evt) {
writeToScreen("Received message: " + evt.data);
//echo_websocket.close();
};
echo_websocket.onerror = function (evt) {
writeToScreen('<span style="color: red;">ERROR:</span> '
+ evt.data);
echo_websocket.close();
};
echo_websocket.onclose = function () {
writeToScreen('<span style="color: red;">CLOSE:</span> ');
};
clearScreen();
}
function init() {
output = document.getElementById("output");
writeToScreen("Connecting to " + url);
createWebsocket();
}
function send_echo() {
if(echo_websocket!=null && echo_websocket.readyState==1)
{
doSend(textID.value);
} else
{
createWebsocket();
//重新连接后,跟着马上发送数据会失败!(我猜测是异步执行的关系)
//得等到 连接成功事件收到后 再发送。
}
}
function closeWebSocket() {
echo_websocket.close();
}
function doSend(message) {
echo_websocket.send(message);
writeToScreen("Sent message: " + message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
function clearScreen(message) {
output.innerHTML="";
}
window.addEventListener("load", init, false);
</script>
</head>
<body>
<h1>Echo Server</h1>
<div style="text-align: left;">
<form action="">
<input onclick="send_echo()" value="发送socket请求" type="button">
<input onclick="closeWebSocket()" value="关闭socket长链接" type="button">
<input id="textID" name="message" value="Hello World, Web Sockets" type="text">
<br>
</form>
</div>
<div id="output"></div>
</body>
</html>
1.1 所需jar包(pom文件引入)
Spring基础jar包:即web项目所需的spring jar包;
Websocket jar包:javax.websocket-ap(注意scope必须为provided,否则runtime会冲突)和spring-websocket。如图:
1.2 所需xml配置如图
注1:path为前台websocket请求路径。
注2:websocket:handshake-interceptors为websocket握手建立连接前自定义拦截器。
注3:WSHandler 为连接建立后的逻辑处理,连接建立后数据传输出错的处理,前台有消息相互传递的逻辑处理,连接关闭后的逻辑处理。
2 设计逻辑
1:jsp建立websocket对象和请求路径
2:握手前获取学校id参数并放入WebSocketSession中
3:WebSocketSession链接成功,把当前WebSocketSession放入集合中
4:后台推送消息——遍历WebSocketSession集合对应学校id的消息json字符串推送到前端页面。
5:消息传输错误——关闭当前WebSocketSession链接,并把它从WebSocketSession集合中移除,重新建立WebSocketSession链接
6:前端页面关闭——关闭当前WebSocketSession链接,并把它从WebSocketSession集合中移除
3 实现代码
3.1 自定义拦截器代码
package com.sunsharing.ihome.air.web.websocket;
import java.util.Map;
import org.apache.log4j.Logger;
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;
/**
* websocket拦截器类
* <p>
* </p>
*
* @author chuhaitao 2017年10月25日 下午2:38:51
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2017年10月25日
* @modify by reason:{方法名}:{原因}
*/
public class WSHandshakeInterceptor implements HandshakeInterceptor {
Logger logger = Logger.getLogger(WSHandshakeInterceptor.class);
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse,
WebSocketHandler webSocketHandler, Exception ex) {
// TODO Auto-generated method stub
}
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse,
WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
// 获取websocket请求地址
String url = serverHttpRequest.getURI().toString();
if (url.indexOf("schoolId") == -1) {// 判断是否有学校id参数
return true;
}
// 获取学校id
String schoolId = url.substring(url.indexOf("=") + 1);
logger.info("获取到学校id:schoolId=" + schoolId);
map.put("schoolId", schoolId);
return true;
}
}
3.2 Websocket处理类代码
package com.sunsharing.ihome.air.web.websocket;
import java.io.IOException;
import java.util.Iterator;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
/**
* websocekt类
* <p>
* </p>
*
* @author chuhaitao 2017年10月23日 上午11:12:48
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2017年10月23日
* @modify by reason:{方法名}:{原因}
*/
public class WSHandler implements WebSocketHandler {
Logger logger = Logger.getLogger(WebSocketHandler.class);
@Autowired
private WSClientManager wsClientManager;
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
wsClientManager.getWebSocketSessions().add(session);
logger.info("链接websocket成功");
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
// 获取消息
String[] messages = message.getPayload().toString().split("&");
// 获取学校id
String schoolId = "大cha";
// 获取要推送的消息
String messageValue = messages[0];
if (messages.length > 1) {
schoolId = messages[0];
messageValue = messages[1];
}
final TextMessage returnMessage = new TextMessage(messageValue);
try {
// 获取所有客户端
Iterator<WebSocketSession> it = wsClientManager.getWebSocketSessions().iterator();
while (it.hasNext()) {
WebSocketSession client = it.next();
if (session != client) {// 判断是否为当前发送消息的客户端
if (client.isOpen()) {
if (schoolId.equals(client.getAttributes().get("schoolId"))) {// 判断是否是同一学校的客户端
client.sendMessage(returnMessage);
}
}
}
}
} catch (IOException e) {
logger.error("推送消息错误", e);
} catch (NullPointerException e) {
logger.error("无客户端,请连接客户端");
} catch (Exception e) {
logger.error("发送错误");
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if (session.isOpen()) {
session.close();
wsClientManager.getWebSocketSessions().remove(session);
}
logger.error("发生错误链接关闭,错误信息为:" + exception.getMessage());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
wsClientManager.getWebSocketSessions().remove(session);
logger.info("websocket链接关闭");
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}
3.3 后端消息推送代码
/**
* 推送消息给指定用户
*
* @author chuhaitao 2017年10月25日 下午2:10:38
* @param schoolId
* @param message
*/
public void sendMessageToUser(String schoolId, String message) {
try {
final Iterator<WebSocketSession> it = wsClientManager.getWebSocketSessions().iterator();
while (it.hasNext()) {
final WebSocketSession client = it.next();
if (client.isOpen()) {
if (schoolId.equals(client.getAttributes().get("schoolId"))) {
final TextMessage textMessage = new TextMessage(message);
client.sendMessage(textMessage);
}
}
}
} catch (final IOException e) {
logger.error("推送消息错误", e);
} catch (final NullPointerException e) {
logger.error("无客户端,请连接客户端");
} catch (final Exception e) {
logger.error("发送错误");
}
}
3.4 WebSocketSession集合代码
package com.sunsharing.ihome.air.web.websocket;
import java.util.HashSet;
import java.util.Set;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
/**
* 发送客户端类
* <p>
* </p>
*
* @author chuhaitao 2017年10月23日 上午11:10:43
* @version V1.0
* @modificationHistory=========================逻辑或功能性重大变更记录
* @modify by user: {修改人} 2017年10月23日
* @modify by reason:{方法名}:{原因}
*/
@Component
public class WSClientManager {
private final Set<WebSocketSession> webSocketSessions = new HashSet<WebSocketSession>();
public Set<WebSocketSession> getWebSocketSessions() {
return webSocketSessions;
}
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Web Socket JavaScript Echo Client</title>
<script language="javascript" type="text/javascript">
var echo_websocket;
var url = ((window.location.protocol == "https:") ? "wss:" : "ws:")
+ "//" + window.location.host
+ "/springweb/websocket.do";
function createWebsocket()
{
echo_websocket = new WebSocket(url);
echo_websocket.onopen = function (evt) {
writeToScreen("Connected !");
//doSend(textID.value);
};
echo_websocket.onmessage = function (evt) {
writeToScreen("Received message: " + evt.data);
//echo_websocket.close();
};
echo_websocket.onerror = function (evt) {
writeToScreen('<span style="color: red;">ERROR:</span> '
+ evt.data);
echo_websocket.close();
};
echo_websocket.onclose = function () {
writeToScreen('<span style="color: red;">CLOSE:</span> ');
};
clearScreen();
}
function init() {
output = document.getElementById("output");
writeToScreen("Connecting to " + url);
createWebsocket();
}
function send_echo() {
if(echo_websocket!=null && echo_websocket.readyState==1)
{
doSend(textID.value);
} else
{
createWebsocket();
//重新连接后,跟着马上发送数据会失败!(我猜测是异步执行的关系)
//得等到 连接成功事件收到后 再发送。
}
}
function closeWebSocket() {
echo_websocket.close();
}
function doSend(message) {
echo_websocket.send(message);
writeToScreen("Sent message: " + message);
}
function writeToScreen(message) {
var pre = document.createElement("p");
pre.style.wordWrap = "break-word";
pre.innerHTML = message;
output.appendChild(pre);
}
function clearScreen(message) {
output.innerHTML="";
}
window.addEventListener("load", init, false);
</script>
</head>
<body>
<h1>Echo Server</h1>
<div style="text-align: left;">
<form action="">
<input onclick="send_echo()" value="发送socket请求" type="button">
<input onclick="closeWebSocket()" value="关闭socket长链接" type="button">
<input id="textID" name="message" value="Hello World, Web Sockets" type="text">
<br>
</form>
</div>
<div id="output"></div>
</body>
</html>
相关文章推荐
- SuperWebSocket发布了第一个测试版
- WebSocket实例教程
- WEBSOCKET协议判断、握手及反馈
- nginx代理websocket服务
- HTML5 WebSocket之HelloWorld
- Asp.net+WebSocket+Emgucv实时人脸识别
- (转发)XCode实现WebSocket
- websocket如何知道mysql数据在变更
- WebSocket不同版本的三种握手方式以及一个Netty实现JAVA类
- 实现WebSocket和WAMP协议的开源库WampSharp
- jquery-websocket
- 在mac系统实现一个websocket常驻进程服务
- 基于html5 WebSocket和WebRTC实现IM和视音频呼叫(二)
- Golang 实现WebSocket服务端,客户端和html5调用
- WEB交互的划时代革新--HTML5中WebSocket应用【1】
- websocket 数据最大长度
- 反向Ajax,第2部分:WebSocket
- Qt:用WebSocket构建QML和C++之间的桥梁
- 使用SuperWebSocket 构建实时 Web 应用
- springboot 整合websocket 站内消息 (支持广播式和只给一人发送)单独信息发送 信息群发 统计连接数