websocket

来源:互联网 发布:淘宝卖家发货怎么打印 编辑:程序博客网 时间:2024/05/21 06:16
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>