`
redstarofsleep
  • 浏览: 439482 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

基于Tomcat的WebSocket(5月8日更新)

 
阅读更多

2014年2月更新: 此API为Tomcat私有,当时Java没有标准API,现在Java有标准API,JSR536,此API不建议使用,新的用法请参照: http://redstarofsleep.iteye.com/blog/1974620

 

之前大概的看过WebSocket,当时Tomcat还不支持WebSocket,所以当时写了一篇基于Jetty的WebSocket实现,地址如下:

http://redstarofsleep.iteye.com/blog/1307608

 

现在Tomcat7.0.27发布了,从这个版本开始Tomcat就支持WebSocket了。

Tomcat的WebSocket和Jetty的大致上差不多,大同小异,这里就简单的贴两个类吧(此例子未考虑多线程的情况):

 

第一个类,这个和Jetty一样,需要一个Servlet来处理WebSocket请求:

package lhc.websocket;

import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;

public class MyWebSocketServlet extends WebSocketServlet {

	private static final long serialVersionUID = -7178893327801338294L;

	@Override
	protected StreamInbound createWebSocketInbound(String arg0) {
System.out.println("##########");
		return new MyMessageInbound();
	}

}

这个Servlet继承自WebSocketServlet,实现createWebSocketInbound方法。该方法返回第二个类的实例。

 

第二个类,处理每一次具体的WebSocket任务:

package lhc.websocket;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import lhc.init.InitServlet;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.WsOutbound;

public class MyMessageInbound extends MessageInbound {

	@Override
	protected void onBinaryMessage(ByteBuffer arg0) throws IOException {
		// TODO Auto-generated method stub
		
	}

	@Override
	protected void onTextMessage(CharBuffer msg) throws IOException {
		for (MessageInbound messageInbound : InitServlet.getSocketList()) {
			CharBuffer buffer = CharBuffer.wrap(msg);
			WsOutbound outbound = messageInbound.getWsOutbound();
			outbound.writeTextMessage(buffer);
			outbound.flush();
		}
		
	}

	@Override
	protected void onClose(int status) {
		InitServlet.getSocketList().remove(this);
		super.onClose(status);
	}

	@Override
	protected void onOpen(WsOutbound outbound) {
		super.onOpen(outbound);
		InitServlet.getSocketList().add(this);
	}
	
	

}

 

 这个类继承自MessageInbound类,必须实现onBinaryMessage和onTextMessage方法。Jetty中只有一个onMessage方法,而Tomcat细化成了2个方法。

 

还要一个初始化的Servlet

package lhc.init;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

import org.apache.catalina.websocket.MessageInbound;

public class InitServlet extends HttpServlet {

	private static final long serialVersionUID = -3163557381361759907L;
	
	private static List<MessageInbound> socketList;  
    
    public void init(ServletConfig config) throws ServletException {  
        InitServlet.socketList = new ArrayList<MessageInbound>();  
        super.init(config);  
        System.out.println("Server start============");  
    }  
      
    public static List<MessageInbound> getSocketList() {  
        return InitServlet.socketList;  
    }  
}

 

 

最后,web.xml中进行一下Servlet的配置。

<?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:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>wsoc</display-name>
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>
  
  <servlet>
  	<servlet-name>mywebsocket</servlet-name>
  	<servlet-class>lhc.websocket.MyWebSocketServlet</servlet-class>
  </servlet>
  <servlet-mapping>
  	<servlet-name>mywebsocket</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
  <servlet>
  	<servlet-name>initServlet</servlet-name>
  	<servlet-class>lhc.init.InitServlet</servlet-class>
  	<load-on-startup>1</load-on-startup>
  </servlet>
</web-app>

 

 

页面的话,就是标准的HTML5的websocket,这个和服务器是否是Tomcat或者Jetty是无关的:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Index</title>
<script type="text/javascript">
var ws = null;
function startWebSocket() {
	if ('WebSocket' in window)
		ws = new WebSocket("ws://localhost:8080/wsoc/mywebsocket.do");
	else if ('MozWebSocket' in window)
		ws = new MozWebSocket("ws://localhost:8080/wsoc/mywebsocket.do");
	else
		alert("not support");
	
	
	ws.onmessage = function(evt) {
		alert(evt.data);
	};
	
	ws.onclose = function(evt) {
		alert("close");
	};
	
	ws.onopen = function(evt) {
		alert("open");
	};
}

function sendMsg() {
	ws.send(document.getElementById('writeMsg').value);
}
</script>
</head>
<body onload="startWebSocket();">
<input type="text" id="writeMsg"></input>
<input type="button" value="send" onclick="sendMsg()"></input>
</body>
</html>

 

 转载请注明出处

17
0
分享到:
评论
71 楼 Ares_Basic 2014-09-21  
楼主 能把你的demo发一份吗 我测试几遍都是404  可能是我的服务器没有配置好
你是否写了服务器配置了
最近写个项目很紧 谢谢搂主了

695529633@qq.com
70 楼 redstarofsleep 2014-02-27  
pch272215690 写道
没想到现在这篇文章还有那么多人在看,
当初我也是参考这个来做了第一个推送的功能。

现在建议大家别用和容器相关联的api,tomcat自己都已经废弃了,
使用javaee标准的api,tomcat已经支持,去官网下载例子看。

楼主苏州的啊,握爪!

是的,我也没想到,标准的API的那篇看的人反而没这个多.
http://redstarofsleep.iteye.com/blog/1974620
69 楼 pch272215690 2014-02-26  
没想到现在这篇文章还有那么多人在看,
当初我也是参考这个来做了第一个推送的功能。

现在建议大家别用和容器相关联的api,tomcat自己都已经废弃了,
使用javaee标准的api,tomcat已经支持,去官网下载例子看。

楼主苏州的啊,握爪!
68 楼 408532231 2014-02-26  
- The type MessageInbound is deprecated
- The hierarchy of the type MInBoundHandlers is inconsistent
楼主  继承了MessageInbound 发现用不了怎么办,jar都没问题的
67 楼 redstarofsleep 2014-02-25  
qiushunyi 写道
楼主你好 我用你的方式建了一个项目,在本地浏览器都可以实现会话,可是我用别的机子局域网访问时,就提示close了,请问这是为什么呢

"ws = new WebSocket("ws://localhost:8080/wsoc/mywebsocket.do"); "你是不是照这个写localhost了,要写IP
66 楼 qiushunyi 2014-02-24  
楼主你好 我用你的方式建了一个项目,在本地浏览器都可以实现会话,可是我用别的机子局域网访问时,就提示close了,请问这是为什么呢
65 楼 redstarofsleep 2013-11-05  
smilelhh 写道
不对呀,我的用http://127.0.0.1:8080/demo/websocket.html就报浏览器不支持。
换成file:///E:/serial/webRoot/websocket.html这种方式可以。
这是什么情况呀??
我用的是360浏览器!

你这种情况确实很奇怪,没有遇到过.
建议您不要用山寨浏览器吧,因为不清楚它用的内核是什么版本的,是否支持,它自己是否还有一些其它的实现,这些都不得而知.
64 楼 smilelhh 2013-11-04  
不对呀,我的用http://127.0.0.1:8080/demo/websocket.html就报浏览器不支持。
换成file:///E:/serial/webRoot/websocket.html这种方式可以。
这是什么情况呀??
我用的是360浏览器!
63 楼 redstarofsleep 2013-09-22  
hywill 写道
楼主你好,请问项目运行后提示close是怎么回事?服务器如何打开呢?

你的Tomcat,浏览器是什么版本?最好都用最新的.
62 楼 hywill 2013-09-22  
楼主你好,请问项目运行后提示close是怎么回事?服务器如何打开呢?
61 楼 redstarofsleep 2013-08-20  
java_MagicWang 写道
java_MagicWang 写道
八月 19, 2013 4:06:19 下午 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [mywebsocket] in context with path [/WebSocket] threw exception [Servlet execution threw an exception] with root cause
java.lang.AbstractMethodError: org.apache.catalina.websocket.WebSocketServlet.createWebSocketInbound(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;)Lorg/apache/catalina/websocket/StreamInbound;
	at org.apache.catalina.websocket.WebSocketServlet.doGet(WebSocketServlet.java:121)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:88)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:722)
请问这是什么原因造成的?tomcat-7.0.37,已经有了
 permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.websocket";

是不是和你配了Struts有关,<url-pattern>*.do</url-pattern>我的例子里用的.do结尾的,你的struts是不是也是?你可以不用.do匹配websocket试试.
60 楼 java_MagicWang 2013-08-19  
java_MagicWang 写道
八月 19, 2013 4:06:19 下午 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [mywebsocket] in context with path [/WebSocket] threw exception [Servlet execution threw an exception] with root cause
java.lang.AbstractMethodError: org.apache.catalina.websocket.WebSocketServlet.createWebSocketInbound(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;)Lorg/apache/catalina/websocket/StreamInbound;
	at org.apache.catalina.websocket.WebSocketServlet.doGet(WebSocketServlet.java:121)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:88)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:722)
请问这是什么原因造成的?tomcat-7.0.37,已经有了
 permission java.lang.RuntimePermission "accessClassInPackage.org.apache.catalina.websocket";
59 楼 java_MagicWang 2013-08-19  
八月 19, 2013 4:06:19 下午 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [mywebsocket] in context with path [/WebSocket] threw exception [Servlet execution threw an exception] with root cause
java.lang.AbstractMethodError: org.apache.catalina.websocket.WebSocketServlet.createWebSocketInbound(Ljava/lang/String;Ljavax/servlet/http/HttpServletRequest;)Lorg/apache/catalina/websocket/StreamInbound;
	at org.apache.catalina.websocket.WebSocketServlet.doGet(WebSocketServlet.java:121)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:88)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:96)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
	at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
	at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
	at java.lang.Thread.run(Thread.java:722)
58 楼 Sherlocked 2013-04-03  
redstarofsleep 写道
bin381 写道
yueool 写道
bin381 写道
你好我想问一下如果得到session呢。我使用login.action在session记录了用户名,那么如何在chat.ws如果取得这个session呢?

博主的代码是在TOMCAT7.0.27下写的,如果你用TOMCAT7.0.32就方便多了
区别在这里 在extends WebSocketServlet时实现了一个方法(7.0.32)
protected StreamInbound createWebSocketInbound(String arg0,HttpServletRequest request) {
// TODO Auto-generated method stub
returnnew MyMessageInbound();;
}

注意,比7.0.27多了一个参数HttpServletRequest request,这个request你就拿去用吧


我试过还是不行的哟。我觉得是使用的协议不一样,那么request会不一样。一个是http的请求,一个是ws的请求


我试过了可以的,Tomcat7.0.35,通过request拿Session正常!

可以贴一下您的代码么?取session的。我也遇到了取不到session的问题。谢谢!!
57 楼 redstarofsleep 2013-01-28  
bin381 写道
yueool 写道
bin381 写道
你好我想问一下如果得到session呢。我使用login.action在session记录了用户名,那么如何在chat.ws如果取得这个session呢?

博主的代码是在TOMCAT7.0.27下写的,如果你用TOMCAT7.0.32就方便多了
区别在这里 在extends WebSocketServlet时实现了一个方法(7.0.32)
protected StreamInbound createWebSocketInbound(String arg0,HttpServletRequest request) {
// TODO Auto-generated method stub
returnnew MyMessageInbound();;
}

注意,比7.0.27多了一个参数HttpServletRequest request,这个request你就拿去用吧


我试过还是不行的哟。我觉得是使用的协议不一样,那么request会不一样。一个是http的请求,一个是ws的请求


我试过了可以的,Tomcat7.0.35,通过request拿Session正常!
56 楼 yaolifei 2013-01-25  
我心非石 写道
很感谢博主分享,发送文字的可以了,我想问一下博主知道怎么传送文件和二进制吗?

目前还不支持二进制
55 楼 我心非石 2013-01-24  
很感谢博主分享,发送文字的可以了,我想问一下博主知道怎么传送文件和二进制吗?
54 楼 cooldean 2012-12-31  
It is a wonderful example, and it works. Thanks for sharing.
53 楼 bin381 2012-12-06  
yueool 写道
bin381 写道
你好我想问一下如果得到session呢。我使用login.action在session记录了用户名,那么如何在chat.ws如果取得这个session呢?

博主的代码是在TOMCAT7.0.27下写的,如果你用TOMCAT7.0.32就方便多了
区别在这里 在extends WebSocketServlet时实现了一个方法(7.0.32)
protected StreamInbound createWebSocketInbound(String arg0,HttpServletRequest request) {
// TODO Auto-generated method stub
returnnew MyMessageInbound();;
}

注意,比7.0.27多了一个参数HttpServletRequest request,这个request你就拿去用吧


我试过还是不行的哟。我觉得是使用的协议不一样,那么request会不一样。一个是http的请求,一个是ws的请求
52 楼 yueool 2012-11-28  
redstarofsleep 写道


重写doGet方法,在这个方法里做判断,如果满了20人就不调用super.doGet(),这时createWebSocketInbound方法根本不会被调用,也就不会去创建WebSocket了,如果还不满20人,要调用super.doGet();

还有一种方法是先让他连上来,然后再MessageInbound类的onOpen方法里面判断,满了还可以发个消息给这个客户端,然后再调用onClose()方法,强制把这个客户端断开。但是这个onClose()要传一个整型的status参数的,我现在还没有找到该传几回去。。


经测试,好用,谢谢

相关推荐

Global site tag (gtag.js) - Google Analytics