SpringMVC结合WebSocket项目开发问题总结
2017-03-29 16:25
302 查看
webScoket:基于 TCP 协议,协议名为”ws” “wss” ,建立连接需要握手,客户端(浏览器)首先向服务器(web server)发起一条特殊的http请求,web server解析后生成应答到浏览器,这样子一个websocket连接就建立了,直到某一方关闭连接.
客户端
建立 WebSocket 连接时要发送一个 header 标记了 Upgrade 的 HTTP 请求,表示请求协议升级
浏览器发起websockt连接请求
客户端
建立 WebSocket 连接时要发送一个 header 标记了 Upgrade 的 HTTP 请求,表示请求协议升级
浏览器发起websockt连接请求
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== Sec-WebSocket-Protocol: chat, superchat Sec-WebSocket-Version: 13 Origin: http://example.com[/code]
服务端
服务端在现有的 HTTP 服务器软件和现有的端口上实现 WebSocket 协议,重用现有代码(比如解析和认证这个 HTTP 请求),然后再回一个状态码为 101 的 HTTP 响应完成握手,之后的数据传输与http无关HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk= Sec-WebSocket-Protocol: chat
部分代码如下:import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.annotation.Resource; import javax.websocket.CloseReason; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.SpringConfigurator; import com.alibaba.fastjson.JSONObject; import com.figure.mysight.cache.DataCache; import com.figure.mysight.model.Member; import com.figure.mysight.service.IMemberService; import com.figure.mysight.util.JsonUtil; import com.figure.mysight.util.StringUtil; import com.figure.mysight.vo.UserInfo; import lombok.Getter; import lombok.Setter; /** * 类似Servlet的注解mapping。无需在web.xml中配置。 * value="/pushServer" 不能为空,否则报错 * configurator = SpringConfigurator.class是为了使该类可以通过Spring注入。 */ @ServerEndpoint(value = "/pushServer", configurator = SpringConfigurator.class) public class WebsocketEndpoint { @Resource private IUserService userService; private static ConcurrentHashMap<String, Session> sessionMap = new ConcurrentHashMap<String, Session>(); public WebsocketEndpoint() { } @Setter @Getter private Session session; @OnOpen public void onOpen(Session session) { System.out.println("onOpen:::id=" + session.getId() + "的用户开始连接到服务器"); sessionMap.put(session.getId(), session); this.session = session; } @OnMessage public void onMessage(String message, Session session) { try { System.out.println("onMessage:::id=" + session.getId() + "的用户发来数据[" + message + "]"); } catch (Exception e) { e.printStackTrace(); } } @OnClose public void onClose(Session session, CloseReason closeReason) { System.out.println("onClose:::id=" + session.getId() + "的用户已经关闭连接,原因:" + closeReason); sessionMap.remove(session.getId()); } @OnError public void onError(Session session, Throwable throwable) { System.out.println("onError:::id=" + session.getId() + "的用户,连接出错,原因:" + throwable); sessionMap.remove(session.getId()); } /** * @param message */ public void broadcastAll(String type, String message) { Set<Map.Entry<String, Session>> set = sessionMap.entrySet(); for (Map.Entry<String, Session> i : set) { try { i.getValue().getBasicRemote().sendText(packData(type, message)); } catch (Exception e) { e.printStackTrace(); } } } public void boardSingle(String type, List<?> datas, Session session) { try { if (session.isOpen()) { if (datas != null && !datas.isEmpty()) { session.getBasicRemote().sendText(packData(type, JsonUtil.toJson(datas))); } else { session.getBasicRemote().sendText(""); } } } catch (Exception e) { } } public void boardSingle(String type, List<?> datas) { try { if (session.isOpen()) { if (datas != null && !datas.isEmpty()) { session.getBasicRemote().sendText(packData(type, JsonUtil.toJson(datas))); } else { session.getBasicRemote().sendText(""); } } } catch (Exception e) { } } public void boardListToSingle(String type, Object datas, Session session) { try { if (session.isOpen()) { if (datas != null) { session.getBasicRemote().sendText(packData(type, JSONObject.toJSON(datas).toString())); } else { session.getBasicRemote().sendText(""); } } } catch (Exception e) { } } private String packData(String type, String data) { return "{type:'" + type + "',data:'" + data + "'}"; } }
问题1:启动过程中出现以下错误ERROR [http-nio-9090-exec-2] - Failed to find the root WebApplicationContext. Was ContextLoaderListener not used? 29-Mar-2017 15:44:49.808 严重 [http-nio-9090-exec-2] org.apache.coyote.AbstractProtocol$ConnectionHandler.process Error reading request, ignored java.lang.IllegalStateException: Failed to find the root WebApplicationContext. Was ContextLoaderListener not used? at org.springframework.web.socket.server.standard.SpringConfigurator.getEndpointInstance(SpringConfigurator.java:68) at org.apache.tomcat.websocket.pojo.PojoEndpointServer.onOpen(PojoEndpointServer.java:44) at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.init(WsHttpUpgradeHandler.java:133) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:813) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1347) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
原因是:SpringConfigurator上通过ContextLoader来获取SpringContext的,所以在web.xml文件中需要配置ContextLoaderListerner<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
问题2:加入上面的配置后,重新启动服务,再次报错:org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/applicationContext.xml]; nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext.xml] at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:344) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:304) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:181) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:217) at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:188) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:129) at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:613) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:514) at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444) at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326) at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4717) at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5179) at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1419) at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1409) at java.util.concurrent.FutureTask.run(FutureTask.java:266) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at java.lang.Thread.run(Thread.java:745) Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext.xml] at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141) at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330) ... 21 more
原因:ContextLoaderListerner需要一个root context,默认会去加载/WEB-INF/applicationContext.xml,<servlet> <servlet-name>spring</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 配置Spring mvc下的配置文件的位置和名称 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
在servlet之间配置的不能被ContextLoaderListerner加载,但servlet可以加载parent context 或者 root context
解决方法:在web.xml中增加以下配置,在servlet的contextConfigLocation可以去掉<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param>
问题3:在部署的时候,使用nginx反向代理,导致webscoket无法连接,
解决方法:在nginx.conf中加入以下配置:#websocket配置 location ~* /pushServer* { proxy_pass http://server; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_read_timeout 300s; }
相关文章推荐
- 项目开发遇到的问题及其解决.总结
- 项目开发总结:解决多线程窗体与主风格不一致问题
- android项目开发所遇问题总结
- springmvc开发中的乱码问题总结
- 项目总结-SpringMVC细节问题
- moss开发实施过程中遇到的问题总结:项目层面
- 【资料收集】结合个人项目的socket相关细节问题总结
- 最近的android项目开发问题总结
- Android项目《Tom伴你行》开发过程中遇到的问题总结
- 没头没尾--项目开发笔记:项目问题的阶段性总结,下一步…………
- 项目开发中问题总结:网络字节序
- 最近的android项目开发问题总结
- Eclipse rap 富客户端开发总结(2)- rap项目目前的进度和存在的问题
- node开发指南中的microblog项目中遇到的问题总结及解决方法
- 总结最近项目开发中遇到的问题,希望对大家有所帮助!
- 公司项目开发过程中遇到的问题总结!
- web项目开发编码问题总结
- 项目开发、项目管理中遇到的问题总结
- SpringMVC 开发过程问题总结-, 实体关系映射对象属性要用包装类Integer, Long ,Double
- 项目总结-基于SpringMVC的微信公众号开发