Spring-mvc+SockJS实现即时通讯教程,并提供各种浏览器版本的支持

    由于HTTP请求的无状态性,且HTTP请求必须由客户端发起,所以在老版的Web开发中都如果要实现服务端向客户端推送消息都是使用轮询的方式。但这种方式会使服务端的压力过大,因为有N个客户端访问就会有N个客户端不停轮询请求。这样的网站性能必然大打折扣。

    所以HTML5提出了一个新的协议——WebSocket。WebSocket的原理就是客户端通过与服务端的一次握手就建立长久的连接通道,于是当服务器需要向客户端推送消息的时候只需要通过已经建立的会话通道,向需要发送消息的客户端发送消息即可。同样的对于已经建立会话通道的客户端与服务端,客户端也可以通过会话通道发送消息到服务端,也就是说他们是"全双工"的。

     但是由于浏览器的历史遗留性问题,不是所有的浏览器都是支持WebSocket的,尤其是IE10以下,所以才出现了SockJS这样一个框架,它的原理也很简单,就是如果你的浏览器支持WebSocket那么他就使用webSocket协议通信,入股不支持就使用流传输或者轮询的方式,这样也保证了资源的最大利用率。

     下面就是WebSocket的请求头:

     Accept-Encoding:gzip, deflate, sdch
     Accept-Language:zh-CN,zh;q=0.8
     Cache-Control:no-cache
     Connection:Upgrade
     Cookie:JSESSIONID=7F788B04BFFF4186D7387BD9DAA0DDE2
     Host:192.168.1.62:8080
     Origin:http://192.168.1.62:8080
     Pragma:no-cache
     Sec-WebSocket-Extensions:permessage-deflate; client_max_window_bits
     Sec-WebSocket-Key:8O2SDdMk/08Tfg3SuTjzXA==
     Sec-WebSocket-Version:13
     Upgrade:websocket

     可以看到,红色部分就是WebSocket的请求所特有的。

     代码实现:(基于Spring-mvc和sockJS-0.3.4)

     Java代码:

     1.WebSocketConfig----用于配置WebSocket地址

      

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
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
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {

	public WebSocketConfig() {
	}

	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
		/*
		 * 将/websck的请求绑定到systemWebSocketHandler处理器
		 * */
		registry.addHandler(systemWebSocketHandler(), "/websck").addInterceptors(new HandshakeInterceptor());
		System.out.println("registed!");
		registry.addHandler(systemWebSocketHandler(), "/sockjs/websck/info").addInterceptors(new HandshakeInterceptor()).withSockJS();
	}
	@Bean
	public WebSocketHandler systemWebSocketHandler() {
		return new SystemWebSocketHandler();
	}
}

    2.HandshakeInterceptor---握手拦截器,可以再此处限制某些握手请求或生成Log

import java.util.Map;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

/**
 * 

* Title:HandshakeInterceptor *

*

* Description *

* * @author 陆仁杰 * @date 2015年7月27日 */ @Component public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor { @Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map attributes) throws Exception { System.out.println("Before Handshake"); return super.beforeHandshake(request, response, wsHandler, attributes); } @Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) { System.out.println("After Handshake"); super.afterHandshake(request, response, wsHandler, ex); } }



3.SystemWebSocketHandler-------核心类,WebSocket消息处理、主动发消息

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.WebSocketSession;
import com.yzXJ.MQ.SendMessageToMQ;

/**
 * 

* Title:SystemWebSocketHandler *

*

* Description To change this license header, choose License Headers in Project * Properties. To change this template file, choose Tools | Templates and open * the template in the editor. *

* * @author 陆仁杰 * @date 2015年7月27日 */ @Component public class SystemWebSocketHandler implements WebSocketHandler { private static final Logger logger = Logger.getLogger(SystemWebSocketHandler.class); private SendMessageToMQ send = null; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception {//连接建立以后自动调用的方法 logger.info("websocket connection success......"); session.sendMessage(new TextMessage("You have connect successfully!"));//向用户主动推送消息 SendMessageIm.addSessions(session);//用于保存所有连接用户信息 } @Override public void handleMessage(WebSocketSession wss, WebSocketMessage wsm) throws Exception {//用户发送消息会调用的方法 String command = ""; try { if (wsm instanceof PongMessage) {//WebSocket连接会有心跳消息,所以需要过滤 return; } else { command = (String) wsm.getPayload();//接收来自用户的消息 logger.info(command);//打印该消息 } } catch (Exception e) { logger.error("Error message:",e); return; } } @Override public void handleTransportError(WebSocketSession wss, Throwable thrwbl) throws Exception {//异常的处理 if (wss.isOpen()) { SendMessageIm.removeSession(wss);//移除该用户会话 wss.close(); } logger.error("websocket connection closed......handleTransportError" ,thrwbl); } @Override public void afterConnectionClosed(WebSocketSession wss, CloseStatus cs) throws Exception {//连接关闭会调用的方法 logger.error("websocket connection closed......afterConnectionClosed" + cs.getCode() + "reson" + cs.getReason()); if (wss.isOpen()) { SendMessageIm.removeSession(wss); wss.close(); } } @Override public boolean supportsPartialMessages() { return false; } }


      


       以下是JS代码:

       

 var ws = null;
  var url = null;
  var transports = [];

  function setConnected(connected) {
    document.getElementById('connect').disabled = connected;
    document.getElementById('disconnect').disabled = !connected;
    document.getElementById('echo').disabled = !connected;
  }

  function connect() {
    if (!url) {
      alert('Select whether to use W3C WebSocket or SockJS');
      return;
    }
    url="http://192.168.1.62:8080/yzXJ/sockjs/websck/info";
    ws= new SockJS(url, undefined, {protocols_whitelist: transports});//建立连接
    ws.onopen = function() {//连接建立以后自动调用的方法
      alert('open');
      log('Info: connection opened.');
    };
    ws.onmessage = function(event) {//后台有消息自动调用的方法
      alert('Received:' + event.data);
      log('Received: ' + event.data);
    };
    ws.onclose = function(event) {//连接关闭自动调用的方法
      setConnected(false);
      log('Info: connection closed.');
      log(event);
    };
  }
  function sayMarco() {
    ws.send("aaaa");//由客户端向服务器发消息的方法
  }

      注意:如果客户端是IE10以下或者某些不支持WebSocket的浏览器,那么就有可能会出现代码2000的客户端错误,解决办法是将服务器换成Tomcat8 ,并对项目的web.xml进行修改:将web.xml的servlet配置和Filter配置都加上同步支持

true,以下是样例配置:

      


 
	yzXJ
	  
      
        springSecurityFilterChain  
        org.springframework.web.filter.DelegatingFilterProxy  
        true
      
  
      
        springSecurityFilterChain  
        /*  
     
	
		contextConfigLocation
		
		   /WEB-INF/classes/applicationContext.xml
           /WEB-INF/classes/spring-security.xml		
		
	       
     
        log4jConfigLocation  
        /WEB-INF/classes/log4j.properties  
       
      
        log4jRefreshInterval  
        6000  
      
      
          
            org.springframework.web.util.Log4jConfigListener  
             
        
    
    
            default
            *.html
      
	
		CharacterEncodingFilter
		org.springframework.web.filter.CharacterEncodingFilter
		true
		
			encoding
			UTF-8
		
		
			forceEncoding
			true
		
	
		
		CharacterEncodingFilter
		/*
	
	
		org.springframework.web.context.ContextLoaderListener
	

	
		yzXJ
		org.springframework.web.servlet.DispatcherServlet
		1
		true
	
	
		yzXJ
		/
	
	
	    /accident/monitor
	
    
       404
       /WEB-INF/views/error.html
    
	
		30
	


      

       


你可能感兴趣的:(Java,高性能框架,通信协议)