websocket开发

1 Websocket作用

1.1 及时通讯

Web及时通讯可以通过websocket实现。

1.2 轮询

web开发中,有时需要通过轮询(比如时间间隔5秒)去服务器读取数据。
使用HTTP协议向服务器发送request请求也是可以实现的,HTTP协议是非持久化的,单向的网络协议,在建立连接后只允许浏览器向服务器发出请求后,服务器才能返回相应的数据,
而且request的Header是非常长的,为了传输一个很小的数据 需要付出巨大的代价,是很不合算的,占用了很多的宽带。
使用websocket可以消除上面的缺陷,websocket只需要服务器和浏览器通过HTTP协议进行一个握手的动作,然后单独建立一条TCP的通信通道就可以进行数据的传送了。

2 Jar包准备和配置

2.1 所需jar包(pom文件引入)

Spring基础jar包:即web项目所需的spring jar包;
Websocket jar包:javax.websocket-ap(注意scope必须为provided,否则runtime会冲突)和spring-websocket。如图:
在这里插入图片描述

2.2 所需xml配置如图

在这里插入图片描述
在这里插入图片描述
注1:path为前台websocket请求路径。
注2:websocket:handshake-interceptors为websocket握手建立连接前自定义拦截器。
注3:WSHandler 为连接建立后的逻辑处理,连接建立后数据传输出错的处理,前台有消息相互传递的逻辑处理,连接关闭后的逻辑处理。

3 设计逻辑

1:jsp建立websocket对象和请求路径
2:握手前获取标识id参数并放入WebSocketSession中
3:WebSocketSession链接成功,把当前WebSocketSession放入集合中
4:后台推送消息——遍历WebSocketSession集合对应标识id的消息json字符串推送到前端页面。
5:消息传输错误——关闭当前WebSocketSession链接,并把它从WebSocketSession集合中移除,重新建立WebSocketSession链接
6:前端页面关闭——关闭当前WebSocketSession链接,并把它从WebSocketSession集合中移除

4 实现代码

4.1 自定义拦截器代码:

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拦截器类
 */
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;
	}

}

4.2 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类
 */
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 = "xx";
		// 获取要推送的消息
		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;
}

}

4.3 后端消息推送代码

/**
	 * 推送消息给指定用户
	 * 
	 * @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("发送错误");
		}
	}

4.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;

/**
 * 发送客户端类
 */
@Component
public class WSClientManager {
	private final Set<WebSocketSession> webSocketSessions = new HashSet<WebSocketSession>();

	public Set<WebSocketSession> getWebSocketSessions() {
		return webSocketSessions;
	}

}

4.5 前端页面jsp

<%@ 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>  

tip:如果想意外断开之后继续创建websocket,则需要调整一下jsp的顺序。

  <%@ 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";
    	 
    var i = 0;
    echo_websocket = new WebSocket(url);
    echo_websocket.onerror = function (evt) {
        // writeToScreen('<span style="color: red;">ERROR:</span> '
        //               + evt.data);
        console.log("error:" + evt.data);
        i++;
        window.setTimeout(function () {
            console.log("重试" + i + "次");
            createWebsocket();
        }, 30000);
    };

    function createWebsocket() {
        echo_websocket = new WebSocket(url);

        echo_websocket.onopen = function (evt) {
            console.log("Connected !");
            //doSend(textID.value);
        };
        echo_websocket.onmessage = function (evt) {
            var arr = evt.data.split("&");
            var time = arr[0];
            var name = arr[1];
            var address = arr[2];
            console.log("时间:" + time + " 姓名:" + name + " 单位" + address);
            sign(time, address, name);
            //  writeToScreen(signTime + danwei + name);
            //echo_websocket.close();
        };

        echo_websocket.onclose = function () {
            //    writeToScreen('<span style="color: red;">CLOSE:</span> ');
            createWebsocket();
        };

        clearScreen();
    }

    function init() {
        //    output = document.getElementById("content");
        writeToScreen("Connecting to " + url);
        console.log("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);
        console.log("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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值