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支持。
Content:
在Eclipse里新建Maven Project,Archetype选择maven-archetype-webapp,
Goup Id里填域名,例如com.kagula,ArtifactId里填项目的名称例如TestSpring4.
第三步:web.xml
第四步:springmvc-servlet.xml
第五步:log4j.xml
在Java Resource->src/main/resources节点新建log4j文件夹,
在文件夹下添加log4j.xml
第六步:新建Controller
第七步:支持WS用到的三个文件
WSHandler.java
WSClientManager.java
下面这个文件可选,因为目前没有碰到必须要拦截器才能正常的浏览器。
HandshakeInterceptor.java
第八步:测试WebSocket服务的html页面
testWebSocket.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
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。
<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