WebSocket服务端主动向客户端推送数据在项目的实际使用

在项目中,常规都是前端向后端发送请求后,才能获取到后端的数据。但是在一些及时消息的处理上,这样的处理效率有些捉襟见肘;在以往获得即时数据时,比较low的方案就是ajax轮询查询;或者可以使用socket的长连接;但是这些在实际的操作上都比较消耗资源;

而websocket在这方面有效的解决这个问题--WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端,客户端接收到消息可即时对消息进行处理

一些三方推送平台也提供了更为完善的消息推送技术如:GoEasy

以下是websocket整合Spring的实际运用,注释详细自行参考,案例是用户消息推送



1:实现WebSocketConfigure配置自己的socket链接,前台参考此路径可建立链接


package com.tanli.ssm.websocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

	/**
	 * 提供配置自己的websocekt类即请求路径
	 */
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		MyHandler myHandler = new MyHandler();
		registry.addHandler(myHandler(),"/dunHandler").addInterceptors(new WebSocketInterceptor());
	}

		/**
		 * 向spring容器注册javabean由spring容器来管理
		 * @return
		 */
	@Bean
	public WebSocketHandler myHandler(){
		return new MyHandler();
	}
}


2:t建立链接时,websocketInteceptor对封装在WebsocketSession的map进行处理,map存储当前登录的用户id,以便在handler处理类获取发送消息

package com.tanli.ssm.websocket;

import java.util.Map;

import javax.servlet.http.HttpSession;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

/**
 * WebSocket链接时的拦截器
 * @author 01
 */
public class WebSocketInterceptor implements HandshakeInterceptor{
	
	
	/**
	 * 当客户端与服务器端握手之前之前执行的方法
	 * 取出当前存在session的用户信息将dunId,封装到WebSocket对象中的map中;
	 * 由Handler处理器中获取id
	 * @return
	 */
	/*@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
			Map<String, Object> attribute) throws Exception {
			//将增强的request转换httpservletRequest	
		  if (request instanceof ServletServerHttpRequest) {
	            ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
	            HttpSession session = serverHttpRequest.getServletRequest().getSession();
	            if (session != null) {
	                attribute.put("dunId", session.getAttribute("dunId"));
	            }
	        }
		  	//放行
	        return true;
	}*/
	
	//测试类
	@Override
	public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler,
			Map<String, Object> attribute) throws Exception {
			//将增强的request转换httpservletRequest	
		  if (request instanceof ServletServerHttpRequest) {
	            ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
	            HttpSession session = serverHttpRequest.getServletRequest().getSession();
	           /* Dun dun = new Dun();
	            dun.setId(12345);
	            session.setAttribute("dunId", 12345);
	            if (session != null) {
	                attribute.put("dunId", 12345);
	            }*/
	        }
		  	//放行
	        return true;
	}
		
	/**
	 * 与服务器websoket建立握手之后执行的方法
	 */
	@Override
	public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler handler, Exception exception) {
		
	}

	

}


3    WebSocketHandler处理类,不同事件触发不同方法,主要业务处理类

package com.tanli.ssm.websocket;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

/**
 * 处理类:Handler--建立连接,发送消息,关闭连接自动执行
 * @author 01
 *
 */
public class MyHandler extends TextWebSocketHandler{

	 //在线用户列表
    private static final Map<Integer, WebSocketSession> users;
    private static final String DUN_ID = "dunId"; //用户标识
	 //类加载初始化一个map集合,存放用户的websocket对象
	static{
		users = new HashMap<Integer, WebSocketSession>();
	}
	
	/**
     * 获取用户标识,获取websocekt对象的map集合
     * @param session
     * @return
     */
    private Integer getClientId(WebSocketSession session) {
        try {
        		//获取存入websocket的userid
            Integer clientId = (Integer) session.getAttributes().get(DUN_ID);
            return clientId;
        } catch (Exception e) {
            return null;
        }
    }
	
		
	/**
	 * 成功建立连接触发的方法,
	 */
	@Override
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		//取出拦截器放入的dunID,为当前的websoket绑定用户到map
		Integer userId = getClientId(session);
        System.out.println(userId+":链接获得id");
        if (userId != null) {
            users.put(userId, session);
            session.sendMessage(new TextMessage("成功建立socket连接"));
            System.out.println(userId);
            System.out.println(session);
        }
    }
	
	
	/**
	 * 当接收到客户端浏览器后接收的方法
	 */
	@Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) {
        // ...
        System.out.println(message.getPayload());
        WebSocketMessage message1 = new TextMessage("server:"+message);
        try {
            session.sendMessage(message1);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
	
	 /**
     * 发送信息给指定用户
     * @param clientId
     * @param message
     * @return
     * map中根据用户的id获取对应得websoket,发送信息
     */
    public boolean sendMessageToUser(Integer dunId, TextMessage message) {
    	 //获取当前的
        if (users.get(dunId) == null) return false;
        WebSocketSession session = users.get(dunId);
        System.out.println("sendMessage:" + session);
        if (!session.isOpen()) return false;
        try {
            session.sendMessage(message);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
    
    /**
     * 广播信息(发送给所有人)
     * @param message
     * @return
     * 遍历出map中所有的websocket发送在线消息
     */
    public boolean sendMessageToAllUsers(TextMessage message) {
        boolean allSendSuccess = true;
        Set<Integer> dunIds = users.keySet();
        WebSocketSession session = null;
        for (Integer dunId : dunIds) {
            try {
                session = users.get(dunId);
                if (session.isOpen()) {
                    session.sendMessage(message);
                }
            } catch (IOException e) {
                e.printStackTrace();
                allSendSuccess = false;
            }
        }

        return  allSendSuccess;
    }
    
    
    
    /**
     * 当链接发生异常后触发的方法,关闭出错会话的连接,和删除在Map集合中的记录
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
    		//判断当前的链接是否在继续,关闭连接
    	if (session.isOpen()) {
            session.close();
        }
        System.out.println("连接出错");
        users.remove(getClientId(session));
    }
    
    /**
     * 当链接关闭后触发的方法,连接已关闭,移除在Map集合中的记录。
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        System.out.println("连接已关闭:" + status); //当前的状态码,并删除存储在map中的websocket的链接对象
        users.remove(getClientId(session));
    }
    
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
    
    
    
}


前端链接以及消息发送代码,包含socket关闭,开启,以及接受消息各种事件触发


<%@ 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>这是一个jsp的测试类</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/js/jquery-1.8.3.js"></script>
<script type="text/javascript">
	var ws = 
	$(function(){
		
		ws = new WebSocket("ws://localhost:8080/dun/dunHandler");
		
       ws.onopen = function () {
        alert("已经链接")
	    //ws.send("{}");
       }
	   ws.onclose = function () {
	      console.log("onclose");
	   }

	   ws.onmessage = function (msg) {
	   //	alert(msg.data+"这是接收到的内容");
			$("#h1").append("<h4 style='color: royalblue;' >"+msg.data+"</h4>");
	   }
	});
	
	function sendMessage(){
		var messageText = $('#messageText').val();
		$.post('${pageContext.request.contextPath}/sendMessage',{'messageText':messageText},function(data){
			//alert(data.message);
		})
	}
	
</script>

</head>
<body>
	<div align="center" style="width: 100%;height: 100%;background: moccasin;border: 3px solid darkolivegreen;" >
		<h1 align="center">欢迎来到websoket测试页面</h1>
		<div align="center" style="width: 600px;height: 400px;border: 2px solid aquamarine;">
			<div style="width: 100%;height: 10%;border: 2px solid gray;">
				<font style="color: blue;size: a3;">聊天窗口</font>
			</div>
			<div style="width:100%;height:78%;color: blanchedalmond;border: darkgoldenrod solid seagreen;">
				<input type="text" id="message" value="输入的内容" />
				<textarea id="description" name="description"></textarea>
				<span id="h1" style="color: royalblue;" ><h4 >这是什么</h4></span>
			</div>
			<div style="width: 100%;height: 10%;border: 2px solid saddlebrown;">
				<input type="text" name="messageText" id="messageText"  />请输入内容
				<input type="button" value="sendBtn" οnclick="sendMessage()" />
			</div>
		</div>
		
	</div>
</body>
</html>


在controller层业务处理调用,向前端推送消息操作

package com.tanli.ssm.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.socket.TextMessage;

import com.tanli.ssm.pojo.Message;
import com.tanli.ssm.websocket.MyHandler;

@Controller
public class SocketController {
	
		//注入发送消息的Handler类
	private MyHandler myHandler =new MyHandler();
	
	@RequestMapping("/sendMessage")
	@ResponseBody
	public Message sendMessage(String messageText){
		TextMessage textMessage = new TextMessage(messageText);
		boolean hasSend = myHandler.sendMessageToUser(12345, textMessage);
		System.out.println(hasSend);
		Message message = new Message();
		if (hasSend) {
			message.setStatus(1);
			message.setMessage("成功接收");
		}else{
			message.setStatus(0);
			message.setMessage("接收失败");
		}
		
		return message;
	}
}





评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值