一个Spring4 WebSocket群发示例

41 篇文章 1 订阅
Title:一个Spring4 WebSocket群发示例
Data: 2017-06-06
Author: kagula
Envrionment:
[1]java version "1.8.0_121"
[2]Eclipse Mars.2 J2EE版
[3]Spring 4.3.8.RELEASE
[4]Tomcat 7


Introduction:
  实现的功能
[1]收到http请求后,把请求的内容群发到WebSocket客户端。
[2]返回jsp视图。
[3]log4j支持。

[4]返回json字符串。  


  
Content:

   要让例子跑起来,需要以下八个步骤:


第一步:新建空白工程
在Eclipse里新建Maven Project,Archetype选择maven-archetype-webapp,
Goup Id里填域名,例如com.kagula,ArtifactId里填项目的名称例如TestSpring4.

结束向导后,Build Path->Add Library->Apache Tomcat v7.0。


第二步:pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.kagula</groupId>
  <artifactId>TestSpring4</artifactId>
  <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>TestSpring4 Maven Webapp</name>
  <url>http://maven.apache.org</url>  
  
  <properties>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	<spring.version>4.3.8.RELEASE</spring.version>
  </properties>
  
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    
    <!-- log4j -->
	<dependency>
	   <groupId>org.slf4j</groupId>
	   <artifactId>slf4j-log4j12</artifactId>
	   <version>1.7.2</version>
	</dependency>
    
    <!-- for support web socket -->
    <dependency>
    	<groupId>javax.websocket</groupId>
    	<artifactId>javax.websocket-api</artifactId>
    	<version>1.1</version>
    	<scope>provided</scope> <!-- 注意,scope必须为provided,否则runtime会冲突,如果使用tomcat 8,还需要将TOMCAT_HOME/lib下的javax.websocket-api.jar一并删除 -->
	</dependency>
	    
	<dependency>
    	<groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
        <version>${spring.version}</version>
    </dependency>
    
    <!-- for support mvc -->
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-core</artifactId>
		<version>${spring.version}</version>
	</dependency>
	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-tx</artifactId>
		<version>${spring.version}</version>
	</dependency>
	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-test</artifactId>
		<version>${spring.version}</version>
	</dependency>
	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-beans</artifactId>
		<version>${spring.version}</version>
	</dependency>
	
	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-aop</artifactId>
		<version>${spring.version}</version>
	</dependency>
	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-orm</artifactId>
		<version>${spring.version}</version>
	</dependency>
	
	<dependency>
		<groupId>org.springframework</groupId>
		<artifactId>spring-webmvc</artifactId>
		<version>${spring.version}</version>
	</dependency>
  </dependencies>
  <build>
    <finalName>TestSpring4</finalName>
    <plugins>  
       <!--  为了解决“Dynamic Web Module 3.0 requires Java 1.6 or newer.”错误需要下面的plugin -->
        <plugin>  
          <groupId>org.apache.maven.plugins</groupId>  
          <artifactId>maven-compiler-plugin</artifactId>  
          <version>3.0</version>  
          <configuration>  
              <source>1.8</source>  
              <target>1.8</target>  
          </configuration>  
        </plugin>  
    </plugins>  
  </build>
</project>



第三步:web.xml
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  
  <context-param>
  	<param-name>webAppRootKey</param-name>
  	<param-value>TestSpring4</param-value>
  </context-param>
  
  <!-- Log4j配置 -->
  <context-param>
  	<param-name>log4jConfigLocation</param-name>
	<param-value>classpath:/log4j/log4j.xml</param-value>
  </context-param>
  <listener>
  	<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
  </listener>
	  
  <servlet>
  	<!-- 使用springmvc这个名称,会指向WEB-INF下的springmvc-servlet.xml配置文件   -->
  	<servlet-name>springmvc</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<load-on-startup>1</load-on-startup>
  </servlet>  
  
  <servlet-mapping>
  	<servlet-name>springmvc</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>



第四步:springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:cache="http://www.springframework.org/schema/cache"
xmlns:c="http://www.springframework.org/schema/c"
xmlns:amq="http://activemq.apache.org/schema/core"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xmlns:jms="http://www.springframework.org/schema/jms"
xmlns:util="http://www.springframework.org/schema/util"   
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
http://www.springframework.org/schema/websocket/spring-websocket.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/data/mongo       
http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd   
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://activemq.apache.org/schema/core 
http://activemq.apache.org/schema/core/activemq-core.xsd
http://www.springframework.org/schema/jms 
http://www.springframework.org/schema/jms/spring-jms.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
         
    <!-- Scan Controller Range -->  
	<context:component-scan base-package="com.kagula.controller" />
	
	<!-- HandlerMapping -->  
    <bean  
        class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping" />  
  
    <!-- HandlerAdapter -->  
    <bean  
        class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter" />
    
	<!-- enable spring mvc annotation driven-->
	<mvc:annotation-driven/>
	    
	<bean id="jspViewResolver"
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/view/" />
		<property name="suffix" value=".jsp" />
	</bean>
	
	<websocket:handlers allowed-origins="*">
	    <!-- 就是ws://localhost:8080/TestSpring4/websocket.do映射到/websocket.do其中TestSpring4是项目名称 -->
        <websocket:mapping path="/websocket.do" handler="demoWSHandler"/>
        <!-- 不自定义拦截器,我的Chrome测试也正常,所以这里注释起来了。
         <websocket:handshake-interceptors>
            <bean class="com.kagula.ws.HandshakeInterceptor"/>
        </websocket:handshake-interceptors> 
         -->
     </websocket:handlers>

    <bean id="demoWSHandler" class="com.kagula.ws.WSHandler"/>
    <bean id="wsClientManager" class="com.kagula.ws.WSClientManager"/>   
</beans>



第五步:log4j.xml
在Java Resource->src/main/resources节点新建log4j文件夹,
在文件夹下添加log4j.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>

<appender name="appendConsole" class="org.apache.log4j.ConsoleAppender">
	<layout class="org.apache.log4j.PatternLayout">
		<param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss:SSS} %l %m%n" />
	</layout>
	<!--限制输出级别 -->
	<filter class="org.apache.log4j.varia.LevelRangeFilter">
		<param name="LevelMax" value="FATAL" />
		<param name="LevelMin" value="DEBUG" />
	</filter>
</appender>

<appender name="myFile" class="org.apache.log4j.RollingFileAppender">          
    <param name="File" value="/log/TestSpring4.log" /><!-- 设置日志输出文件名 --> 
    <!-- Win7 /log/escortcashbox.log 的设置是输出到c:/log/escortcashbox.log -->      
    <!-- 设置是否在重新启动服务时,在原有日志的基础添加新日志 -->       
    <param name="Append" value="false" />       
    <param name="MaxBackupIndex" value="100" />
    <param name="MaxFileSize" value="500KB" />        
    <layout class="org.apache.log4j.PatternLayout">       
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss:SSS} %l %m%n" />       
    </layout>      
</appender>

<logger name="org.springframework">
   <level value="warn" />
   <appender-ref ref="appendConsole"/>
   <appender-ref ref="myFile"/>
</logger>

<root>
	<priority value="debug" />
	<appender-ref ref="appendConsole" />
	<appender-ref ref="myFile"/>
</root>
</log4j:configuration>



第六步:新建Controller
package com.kagula.controller;

import java.io.IOException;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import com.kagula.ws.WSClientManager;

@Controller  
@RequestMapping(value = "/HelloWorld")  
public class HelloWorld {
	private static Logger logger = LoggerFactory.getLogger(HelloWorld.class);
	@Autowired
	private WSClientManager wsClientManager;
	
	//http://localhost:8080/TestSpring4/HelloWorld/testReturnJSON.do
	@RequestMapping(value = "/testReturnJSON.do")
	@ResponseBody
    public Object testReturnJSON(@RequestParam("data") String data) {        
		logger.debug("我的测试<<"+data);
		
        TextMessage toWSMsg = new TextMessage(data);
        

        //把收到的消息群发到WS客户端
		try {
			Iterator<WebSocketSession> it= wsClientManager.setWSS.iterator();
			while(it.hasNext())
			{
				WebSocketSession client = it.next();
				client.sendMessage(toWSMsg);
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
        return "{\"data\":\""+data+"\"}";  
    }
	
	@RequestMapping(value = "/returnModelView.do")  
    public ModelAndView returnModelView() {        
        //如果用户访问的时候不带userID参数,就不会映射到这个handler中。在Web browser中会提示400错误。  
        ModelAndView mav = new ModelAndView();  
        mav.setViewName("returnModelView");  
        //mav.addObject("userID", userID);
        return mav;  
    }  
}



第七步:支持WS用到的三个文件
WSHandler.java
package com.kagula.ws;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;

public class WSHandler implements WebSocketHandler{
	private static Logger logger = LoggerFactory.getLogger(WebSocketHandler.class);
	@Autowired
	private WSClientManager wsClientManager;
	
    @Override  
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {  
        logger.debug("connect to the websocket success......");  
        session.sendMessage(new TextMessage("Server:connected OK!"));
        wsClientManager.setWSS.add(session);
    }  
  
    @Override  
    public void handleMessage(WebSocketSession wss, WebSocketMessage<?> wsm) throws Exception {  
        TextMessage returnMessage = new TextMessage(wsm.getPayload()  
                + " received at server");  
        logger.debug(wss.getHandshakeHeaders().getFirst("Cookie"));  
        wss.sendMessage(returnMessage);  
    }  
  
    @Override
    public void handleTransportError(WebSocketSession wss, Throwable thrwbl) throws Exception {  
        if(wss.isOpen()){  
            wss.close();
            wsClientManager.setWSS.remove(wss);
        }  
       logger.debug("websocket connection closed......");  
    }  
  
    @Override  
    public void afterConnectionClosed(WebSocketSession wss, CloseStatus cs) throws Exception {  
    	logger.debug("websocket connection closed......");  
    	wsClientManager.setWSS.remove(wss);
    }  
  
    @Override  
    public boolean supportsPartialMessages() {  
        return false;  
    }
}



WSClientManager.java
package com.kagula.ws;

import java.util.HashSet;
import java.util.Set;

import org.springframework.web.socket.WebSocketSession;

//这里存放所有可用的Web socket session,用于收到Http请求后群发。
public class WSClientManager {
	//这里只是举个例子,所以不写线程保护代码了。
	public Set<WebSocketSession> setWSS = new HashSet<WebSocketSession>();
}



下面这个文件可选,因为目前没有碰到必须要拦截器才能正常的浏览器。
HandshakeInterceptor.java
package com.kagula.ws;

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;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{
	private static Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);
	
	@Override
    public boolean beforeHandshake(ServerHttpRequest request,
            ServerHttpResponse response, WebSocketHandler wsHandler,
            Map<String, Object> attributes) throws Exception {

        // 解决The extension [x-webkit-deflate-frame] is not supported问题
        if (request.getHeaders().containsKey("Sec-WebSocket-Extensions")) {
            request.getHeaders().set("Sec-WebSocket-Extensions",
                    "permessage-deflate");
        }

        logger.debug("Before Handshake");
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    @Override
    public void afterHandshake(ServerHttpRequest request,
            ServerHttpResponse response, WebSocketHandler wsHandler,
            Exception ex) {
        logger.debug("After Handshake");
        super.afterHandshake(request, response, wsHandler, ex);
    }
}



第八步:测试WebSocket服务的html页面

testWebSocket.html 
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Web Socket JavaScript Echo Client</title>
  <script src="http://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>
  <script language="javascript" type="text/javascript">
    var echo_websocket;
    var wsUri = "ws://localhost:8080/TestSpring4/websocket.do";
    
    function createWebsocket()
    {
        echo_websocket = new WebSocket(wsUri);
        
        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 " + wsUri);
      
      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 οnclick="send_echo()" value="发送socket请求" type="button">
    <input οnclick="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>



测试方式
Step1:
启动服务后,在浏览器中打开testWebSocket.html
正常的话,会提示connect成功。


Step2:在浏览器中输入
http://localhost:8080/TestSpring4/HelloWorld/testReturnJSON.do?data=kagula


Step3:
在浏览器中可以看到testWebSocket.html页面收到了广播数据。




参考资料
[1]《tomcat 7下spring 4.x mvc集成websocket以及sockjs完全参考指南(含nginx/https支持)》
http://www.cnblogs.com/zhjh256/p/6052102.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kagula086

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值