使用Spring的低层级WebSocket API实现即时聊天工具-上篇

spring4.0为websocket通信提供了支持,包括:

  • 发送和接收消息的低层级api
  • 发送和接收消息的高级api
  • 用来发送消息的模板
  • 支持sockJS,用来解决浏览器端、服务器以及代理不支持websocket的问题

我们首先会从如何使用spring的低层级websocket api开始

按照其最简单的形式,websocket只是两个应用端进行通信的通道。位于websocket一端的应用发送消息,另一端接收消息,因为是全双工,所以每一端即可发消息也可以收消息;

websocket最常见的应用场景是在浏览器与服务器的相互通信,通常由客户端的js开启一个到服务器的连接,服务器通过这个连接发送更新给客户端;相比轮询服务器查找更新方案,这种技术更加高效和自然。

接下来我们将采用Spring低层级的WebSocket API制作一个即时聊天工具

运行环境:jdk1.7+tomcat7     框架:spring+springmvc+mybatis

第一步:首先引入支持springsocket所需jar包

		<springframework.version>4.1.7.RELEASE</springframework.version>	<!-- springframework -->
			<!-- springsocke begin-->
			<dependency>
	            <groupId>org.springframework</groupId>
	            <artifactId>spring-websocket</artifactId>
	            <version>${springframework.version}</version>
	        </dependency>
			<dependency>
				<groupId>org.springframework</groupId>
				<artifactId>spring-messaging</artifactId>
				 <version>${springframework.version}</version>
			</dependency>
			<!-- springsocke end-->

第二步:定义ChatHandler消息处理类

package com.platform_manager.t_bizlog.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
/**
 * 消息处理类
 * 扩展AbstractWebSocketHandler类定义ChatHandler处理类
 * 用来在服务端处理通过websocket传送的文本消息
 * 其中AbstractWebSocketHandler包含TextWebSocketHandler和BinaryWebSocketHandler两个子类分别用于处理文本和二进制消息
 * @author   韩慧兵
 * @version  2016年12月6日 下午5:33:23
 * @since    JDK 1.6
 */
public class ChatHandler extends AbstractWebSocketHandler{
	private static final Logger logger=LoggerFactory.getLogger(ChatHandler.class);
	
	/**
	 * 连接建立 ,可用于资源准备
	 * @param session
	 * @throws Exception
	 * @author   韩慧兵
	 * @version  2016年12月2日 上午10:25:21
	 * @since    JDK 1.6
	 */
	public void afterConnectionEstablished(WebSocketSession session) throws Exception {
		logger.info("连接建立成功!");
	}
	
	/**
	 * 重载 hanleTextMessage方法用于文本抵达后,在同一个连接上返回另外一条文本消息
	 * @param session
	 * @param message
	 * @throws Exception
	 * @author   韩慧兵
	 * @version  2016年12月2日 上午10:19:45
	 * @since    JDK 1.6
	 */
	protected void handleTextMessage(WebSocketSession session,TextMessage message) throws Exception {
		logger.info("回复消息:"+message.getPayload());
		Thread.sleep(2000);//模拟延时
		session.sendMessage(new TextMessage("你好,我是服务端,你发送的信息是:"+message.getPayload()));//发送信息
	}
	
	/**
	 * 连接关闭后 ,可用于资源销毁
	 * @param session
	 * @param status
	 * @throws Exception
	 * @author   韩慧兵
	 * @version  2016年12月2日 上午10:24:38
	 * @since    JDK 1.6
	 */
	public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
		logger.info("连接关闭成功,状态为"+status);
	}
}

第三步:定义聊天工具socket握手拦截器

package com.platform_manager.t_bizlog.controller;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
/**
 * 握手拦截器
 * @author   韩慧兵
 * @version  2016年12月6日 下午5:44:54
 * @since    JDK 1.6
 */
public class ChatHandshakeInterceptor implements org.springframework.web.socket.server.HandshakeInterceptor {  
	private static final Logger logger=LoggerFactory.getLogger(ChatHandler.class);
	/**
	 * 握手前拦截 
	 * @param request
	 * @param serverHttpResponse
	 * @param webSocketHandler
	 * @param map
	 * @return
	 * @throws Exception
	 * @author   韩慧兵
	 * @version  2016年12月6日 下午5:49:24
	 * @since    JDK 1.6
	 */
    @Override  
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
    	logger.info("开始握手!");
        return true;  
    }  
  
    @Override  
    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {  
    	logger.info("结束握手!");
    }  
}  

第四步:通过Java配置Spring,启用websocket并映射消息处理器

package com.platform_manager.t_bizlog.controller;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
/**
 * 通过Java配置Spring,启用websocket并映射消息处理器
 * @author   韩慧兵
 * @version  2016年12月6日 下午5:45:08
 * @since    JDK 1.6
 */
@Configuration //配置类  
@EnableWebMvc
@EnableWebSocket //声明支持websocket
public class ChatWebSocketConfig implements WebSocketConfigurer{
	
	/**
	 * 注册消息处理器 
	 * @param registry
	 * @author   韩慧兵
	 * @version  2016年12月2日 上午10:50:26
	 * @since    JDK 1.6
	 */
	@Override
	public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //将marcoHandler()映射至"/marco.do"
        registry.addHandler(chatHandler(),"/chat.do").addInterceptors(new ChatHandshakeInterceptor());  
          
        //将marcoHandler()映射至"/sockjs/marco.do"
        registry.addHandler(chatHandler(),"/sockjs/chat.do").addInterceptors(new ChatHandshakeInterceptor()).withSockJS(); 
	}
	
	/**
	 * 声明MarcoHandler bean
	 * @return
	 * @author   韩慧兵
	 * @version  2016年12月2日 上午10:49:38
	 * @since    JDK 1.6
	 */
	@Bean
	public ChatHandler chatHandler()
	{
		return new ChatHandler();
	}
}

第五步:springmvc中添加对ChatWebSocketConfig类的自动扫描

<context:component-scan base-package="包位置">

第六步:制定客户端聊天窗体界面

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@include file="/mastery/page/common/header.jsp"%>
<title>用户列表</title>
<script src="http://cdn.jsdelivr.net/sockjs/1.0.1/sockjs.min.js"></script>
<script type="text/javascript">
    $(function() {
        var websocket;
        //打开socket
        if ('WebSocket' in window) {
            //alert("WebSocket");  
            websocket = new WebSocket(
                    "ws://localhost:8080/mastery_platform_manager/chat.do");
        } else if ('MozWebSocket' in window) {
            alert("MozWebSocket");
            websocket = new MozWebSocket("ws://echo");
        } else {
            alert("SockJS");
            websocket = new SockJS(
                    "http://localhost:8080/mastery_platform_manager/sockjs/chat.do");
        }

        //处理连接开启事件
        websocket.onopen = function(evnt) {
            $("#tou").html("链接服务器成功!")
        };

        //处理信息
        websocket.onmessage = function(evnt) {
            $("#msg").html($("#msg").html() + "<br/>" + evnt.data);
        };
        websocket.onerror = function(evnt) {
        };

        //处理关闭事件
        websocket.onclose = function(evnt) {
            $("#tou").html("与服务器断开了链接!")
        }
        $('#send').bind('click', function() {
            send();
        });
        function send() {
            if (websocket != null) {
                var message = document.getElementById('message').value;
                websocket.send(message); //调用后台handleTextMessage方法
            } else {
                alert('未与服务器链接.');
            }
        }
    });
</script>

<div class="panel panel-primary">
    <div class="panel-heading">
        <h3 class="panel-title">webSocket即时聊天工具 -【使用Spring低层级WebSocket
            API版】</h3>
    </div>
    <div class="panel-body" id="msg">
        服务器状态:
        <div class="page-header" id="tou"></div>
        聊天信息如下:
    </div>
</div>
<p>
<div class="col-lg">
    <div class="input-group">
        <input type="text" class="form-control" placeholder="发送信息..."
            id="message"> <span class="input-group-btn">
            <button class="btn btn-default" type="button" id="send">发送</button>
        </span>
    </div>
</div>
<%@include file="/mastery/page/common/footer.jsp"%>
实现效果如下:

附:

笔者在编写示例中,遇到了很多问题,查看了各种偏方

1、jdk

What you’ll need
    About 15 minutes
    A favorite text editor or IDE
    JDK 1.8 or later
    Gradle 2.3+ or Maven 3.0+

摘自spring官网:http://spring.io/guides/gs/messaging-stomp-websocket/

官网描述为1.8+,笔者亲测1.7.0可以使用,最开始笔者采用1.6测试,会遇到如下问题提示

信息: JSR 356 WebSocket (Java WebSocket 1.0) support is not available when running on Java 6. To suppress this message, run Tomcat on Java 7, remove the WebSocket JARs from $CATALINA_HOME/lib or add the WebSocketJARs to the tomcat.util.scan.DefaultJarScanner.jarsToSkip property in $CATALINA_BASE/conf/catalina.properties. Note that the deprecated Tomcat 7 WebSocket API will be available.

2、web.xml

       网上有很多文章要求修改xml的申明,笔者并未修改,笔者web.xml声明如下

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">

其他

1、ws:// 表明这是个基本的websocket连接,如果是安全websocket的话,协议的前缀将会是wss://

2、服务端关闭连接浏览器转向其他页面都会关闭连接

3、防火墙代理通常会限制除http以外的流量,他们有可能不支持或还没有配置websocket通信

4、Sockjs是websocket技术的一种模拟,它的底层非常智能,sockjs会优先选用websocket,如果websocket不支持的话他会从以下方案中挑选最优的可行方案

    XHR流、XDR流、iFrame事件源、iFrame HTML文件、XHR轮询、XDR轮询、iFrame XHR轮询、JSONP轮询

   Websocket提供了浏览器和服务器之间的通信方式,当运行环境不支持webscoket的时候,socket提供了备选方案。但不管哪种场景,该篇文章所描述的通信方式在实际应用中都显得层级过低。下一篇,我们将在websocket之上使用stomp,为浏览器和服务器之间的通信添加恰当的消息语义



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值