`
zzc1684
  • 浏览: 1224344 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

tomcat7.027的webSocket实现

阅读更多

前几天研究了一下tomcat7.027的webSocket实现。简单看了下官方源码自己实践了一下。

在这里简单介绍一下tomcat的webSocketAPI使用。

在这里啰嗦几句:

很多朋友听说webSocket不知道是什么。知道是什么不知道怎么用,知道怎么用不知道具体实现。其实我当初也是这样。

实际上webSocket可以简单的理解为用浏览器与服务器简历socket连接,但是用了一个特殊的协议,偶收协议,它与http协议发送的报头不一样。

websocket需要服务器和浏览器支持,浏览器不支持,也 就无法使用这个技术。服务器可以自己实现协议连接,但是我们不准备自己实现(其实看需求,至少对我来说不需要),当然目前javaEE官方不支持这个实 现,没有规范(据说jsr356准备支持,期待来年【2013】javaEE7吧)

目前实现的java服务端第三方webSocketAPI不算少,比如jetty就是一种(多的我也举例不了,我只知道,没研究过有多少实现。)tomcat也自带了实现API

webSocket想要手动实现比较麻烦,可以看下tomcat实现过程,大致都一样。

总之一句话,webSocket是一种客户端与服务端连接socket的技术,实现即时消息,取代comet但是并没广泛只用,因为大多需要浏览器的支持,相对comet有很多优点,此处不举例说明。可以自己google一下。

 

tomcat7.027如何实现webSocket程序:

总的来说,实现webSocket的servlet要继承WebSocketServlet这个类。这个类是tomcat自己包装的servlet。

所有的入口都在protected StreamInbound createWebSocketInbound(String subProtocol) {}这个方法。 也就是说,我们实现这个方法,就可以实现握手协议了。

注意看这个方法。 要求返回StreamInbound类型。这个类型我们需要继承自己实现。打开源码观看这个类

有如下方法

 

Java代码  收藏代码
  1. /** 
  2.     * Intended to be overridden by sub-classes that wish to be notified 
  3.     * when the outbound connection is established. The default implementation 
  4.     * is a NO-OP. 
  5.     * 
  6.     * @param outbound    The outbound WebSocket connection. 
  7.     */  
  8.    protected void onOpen(WsOutbound outbound) {  
  9.        // NO-OP  
  10.    }  
  11.   
  12.    /** 
  13.     * Intended to be overridden by sub-classes that wish to be notified 
  14.     * when the outbound connection is closed. The default implementation 
  15.     * is a NO-OP. 
  16.     * 
  17.     * @param status    The status code of the close reason. 
  18.     */  
  19.    protected void onClose(int status) {  
  20.        // NO-OP  
  21.    }  
  22.   
  23.   
  24.    /** 
  25.     * This method is called when there is a binary WebSocket message available 
  26.     * to process. The message is presented via a stream and may be formed from 
  27.     * one or more frames. The number of frames used to transmit the message is 
  28.     * not made visible to the application. 
  29.     * 
  30.     * @param is    The WebSocket message 
  31.     * 
  32.     * @throws IOException  If a problem occurs processing the message. Any 
  33.     *                      exception will trigger the closing of the WebSocket 
  34.     *                      connection. 
  35.     */  
  36.    protected abstract void onBinaryData(InputStream is) throws IOException;  
  37.   
  38.   
  39.    /** 
  40.     * This method is called when there is a textual WebSocket message available 
  41.     * to process. The message is presented via a reader and may be formed from 
  42.     * one or more frames. The number of frames used to transmit the message is 
  43.     * not made visible to the application. 
  44.     * 
  45.     * @param r     The WebSocket message 
  46.     * 
  47.     * @throws IOException  If a problem occurs processing the message. Any 
  48.     *                      exception will trigger the closing of the WebSocket 
  49.     *                      connection. 
  50.     */  
  51.    protected abstract void onTextData(Reader r) throws IOException;  

 

 上面的方法都是要我们自己实现的。tomcat没有给我们实现。

仔细看都是onXxx格式,类似事件监听。其实也差不多。只是tomcat在得到消息或者链接发生变化的时候会去调用这些方法,实现方法“自动”触发。

仔细看源码还有很多函数可以使用,这里不一一介绍。感兴趣可以打开源码看看。

其实仔细看官方的例子,chat那个例子也能得到这个结论(tomcat的webSocket例子需要tomcat7.027才带有)

我们定义一个servlet

 

Java代码  收藏代码
  1. @WebServlet(urlPatterns = { "/chatWebSocket" })  
  2. public class ChatWebSocketServlet extends WebSocketServlet {  
  3.   
  4.     private static final long serialVersionUID = 1L;  
  5.     OnLineUser theUser;  
  6.   
  7.     @Override  
  8.     protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
  9.             throws ServletException, IOException {  
  10.         theUser = (OnLineUser) req.getSession().getAttribute("loginUser");  
  11.         super.doGet(req, resp);  
  12.     }  
  13.   
  14.     @Override  
  15.     protected StreamInbound createWebSocketInbound(String subProtocol) {  
  16.         return new ChatMessageInbound(theUser);  
  17.     }  
  18.   
  19. }  

 

 doget不用说,是连接的开始,然后取出登录的用户,这个是为了管理连接使用的,你在看这个例子的时候不需要doget方法和theUser声 明,只要有createWebSocketInbound方法就行。上面说了。这个方法是webSocket的入口。其实也是 WebSocketServlet这个类写好的doget,我们看WebSocketServlet的doget是如何写的

 

Java代码  收藏代码
  1. @Override  
  2.    protected void doGet(HttpServletRequest req, HttpServletResponse resp)  
  3.            throws ServletException, IOException {  
  4.   
  5.        // Information required to send the server handshake message  
  6.        String key;  
  7.        String subProtocol = null;  
  8.        List<String> extensions = Collections.emptyList();  
  9.   
  10.        if (!headerContainsToken(req, "upgrade""websocket")) {  
  11.            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);  
  12.            return;  
  13.        }  
  14.   
  15.        if (!headerContainsToken(req, "connection""upgrade")) {  
  16.            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);  
  17.            return;  
  18.        }  
  19.   
  20.        if (!headerContainsToken(req, "sec-websocket-version""13")) {  
  21.            resp.setStatus(426);  
  22.            resp.setHeader("Sec-WebSocket-Version""13");  
  23.            return;  
  24.        }  
  25.   
  26.        key = req.getHeader("Sec-WebSocket-Key");  
  27.        if (key == null) {  
  28.            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);  
  29.            return;  
  30.        }  
  31.   
  32.        String origin = req.getHeader("Origin");  
  33.        if (!verifyOrigin(origin)) {  
  34.            resp.sendError(HttpServletResponse.SC_FORBIDDEN);  
  35.            return;  
  36.        }  
  37.   
  38.        List<String> subProtocols = getTokensFromHeader(req,  
  39.                "Sec-WebSocket-Protocol-Client");  
  40.        if (!subProtocols.isEmpty()) {  
  41.            subProtocol = selectSubProtocol(subProtocols);  
  42.   
  43.        }  
  44.   
  45.        // TODO Read client handshake - Sec-WebSocket-Extensions  
  46.   
  47.        // TODO Extensions require the ability to specify something (API TBD)  
  48.        //      that can be passed to the Tomcat internals and process extension  
  49.        //      data present when the frame is fragmented.  
  50.   
  51.        // If we got this far, all is good. Accept the connection.  
  52.        resp.setHeader("upgrade""websocket");  
  53.        resp.setHeader("connection""upgrade");  
  54.        resp.setHeader("Sec-WebSocket-Accept", getWebSocketAccept(key));  
  55.        if (subProtocol != null) {  
  56.            resp.setHeader("Sec-WebSocket-Protocol", subProtocol);  
  57.        }  
  58.        if (!extensions.isEmpty()) {  
  59.            // TODO  
  60.        }  
  61.   
  62.        // Small hack until the Servlet API provides a way to do this.  
  63.        StreamInbound inbound = createWebSocketInbound(subProtocol);  
  64.        ((RequestFacade) req).doUpgrade(inbound);  
  65.    }  

 

注意倒数第三行,调用了createWebSocketInbound方法,我们重写这个方法。

 

Java代码  收藏代码
  1. @Override  
  2.     protected StreamInbound createWebSocketInbound(String subProtocol) {  
  3.         return new ChatMessageInbound(theUser);  
  4.     }  

上面的ChatMessageInbound是我自己定义的继承类。

 

Java代码  收藏代码
  1. public final class ChatMessageInbound extends MessageInbound {  
  2.   
  3.     public ChatMessageInbound(OnLineUser theUser) {  
  4.         this.theUser = theUser;  
  5.     }  
  6.   
  7.     @Override  
  8.     protected void onOpen(WsOutbound outbound) {  
  9.         // 添加链接到容器  
  10.         ChatMessageInbound theBound = this;  
  11.         ChatContainer.addInbound(theBound.theUser, theBound);  
  12.         // 向每个在线用户发送消息  
  13.         ChatContainer.eachAllBound(new ContainerCallBack() {  
  14.             @Override  
  15.             public void eachCallBack(ChatMessageInbound theBound, OnLineUser theUser) {  
  16.                 ListUserMsg listUserMsg = new ListUserMsg(ChatContainer.getUserList());  
  17.                 WriteTookit.writeToBound(theBound, listUserMsg.toMsg());  
  18.             }  
  19.         });  
  20.     }  
  21.   
  22.     @Override  
  23.     protected void onClose(int status) {  
  24.         ChatContainer.removeInbound(theUser);  
  25.     }  
  26.   
  27.     @Override  
  28.     protected void onBinaryMessage(ByteBuffer message) throws IOException {  
  29.     }  
  30.   
  31.     @Override  
  32.     protected void onTextMessage(CharBuffer message) throws IOException {  
  33. //      CHAT_MODEL.setMessage(message.toString());  
  34. //      ChatContainer.eachAllBound(new ContainerCallBack() {  
  35. //          @Override  
  36. //          public void eachCallBack(ChatMessageInbound theBound, OnLineUser theUser) {  
  37. //              WriteTookit.writeToBound(theBound, CHAT_MODEL.getSayMsg());  
  38. //          }  
  39. //      });  
  40.     }  
  41.   
  42.     // 变量区域  
  43.     private OnLineUser theUser;  
  44. }  

 这里只是简单实现了一下,注释部分只是处理这个方法的部分,那里是一个容器,存档所有在线用户。并且提供遍历插入以及删除等方法,在自己实现的时候完全不需要这么写。

下面是容器代码

 

Java代码  收藏代码
  1. public final class ChatContainer {  
  2.     /** 
  3.      * 保存服务器连接的用户的容器 
  4.      */  
  5.     private static final Map<OnLineUser, ChatMessageInbound> CHAT_MAP = new HashMap<OnLineUser, ChatMessageInbound>();  
  6.   
  7.     /** 
  8.      * 取出用户的连接 
  9.      */  
  10.     public static ChatMessageInbound getInbound(OnLineUser theUser) {  
  11.         return CHAT_MAP.get(theUser);  
  12.     }  
  13.   
  14.     /** 
  15.      * 放入一个连接 
  16.      */  
  17.     public static void addInbound(OnLineUser theUser,  
  18.             ChatMessageInbound outbound) {  
  19.         CHAT_MAP.put(theUser, outbound);  
  20.         System.out.println(CHAT_MAP.size());  
  21.     }  
  22.   
  23.     /** 
  24.      * 移除一个连接 
  25.      *  
  26.      * @param theUser 
  27.      * @return 
  28.      */  
  29.     public static ChatMessageInbound removeInbound(OnLineUser theUser) {  
  30.         return CHAT_MAP.remove(theUser);  
  31.     }  
  32.   
  33.     /** 
  34.      * 遍历所有连接 
  35.      */  
  36.     public static void eachAllBound(ContainerCallBack callBackInter) {  
  37.         Iterator<OnLineUser> keyIter = CHAT_MAP.keySet().iterator();  
  38.         while (keyIter.hasNext()) {  
  39.             OnLineUser theUser = keyIter.next();  
  40.             callBackInter.eachCallBack(CHAT_MAP.get(theUser), theUser);  
  41.         }  
  42.     }  
  43.   
  44.     /** 
  45.      * 回调函数的接口 
  46.      *  
  47.      * @author WangZhenChong 
  48.      */  
  49.     public interface ContainerCallBack {  
  50.         void eachCallBack(ChatMessageInbound theBound, OnLineUser theUser);  
  51.     }  
  52.   
  53. }  

 

 

我定义了一种数据交约定,使用json 字符串,MsgType表示消息类型,类似windows的消息机制

 

Java代码  收藏代码
  1. /** 
  2.  * 前台和后台交互的信息类型常量 
  3.  *  
  4.  * @author WangZhenChong 
  5.  *  
  6.  */  
  7. public final class MsgTypeConstants {  
  8.     public static short GET_USER_LIST = 1;// 在线所有用户信息交互  
  9.     public static short SEND_ONE_TO_ONE = 2;// 对一个用户发送消息  
  10.     public static short SEND_ONE_TO_ALL = 3;// 对所有用户发送消息  
  11.     public static short SEND_SERVER_MSG = 4;// 发送系统消息  
  12. }  

 余下的msgContent就是消息内容,比如列出现在用户这个内容就是[...,...,...,...]发送消息就是消息模型的内容。

这样解决单通道多操作的方法。

 

 

下面列出前台js核心内容。

使用jquery

 

Js代码  收藏代码
  1. $(document).ready(function() {  
  2.     $("#connBtn").bind('click'function() {  
  3.         $.ajax({  
  4.             url : "/tomcatWebSocket/Login#?asdasdasd",  
  5.             type : "POST",  
  6.             processData : false,  
  7.             data : $.param({  
  8.                         username : document.getElementById("usernameField").value  
  9.                     }),  
  10.             success : function(msg, status) {  
  11.                 initChat();  
  12.                 initUserList();  
  13.                 $("#sendBtn").removeAttr("disabled");  
  14.                 $("#connBtn").attr("disabled""disabled");  
  15.                 $("#usernameField").attr("disabled""disabled");  
  16.             },  
  17.             error : function(jqXHR, textStatus, errorThrown) {  
  18.                 alert("服务器内部错误");  
  19.             }  
  20.         });  
  21.   
  22.     });  
  23.       
  24.   
  25. var Chat = {};  
  26. Chat.socket = null;  
  27. function initChat() {  
  28.     var wsURL = 'ws://' + window.location.host  
  29.             + '/tomcatWebSocket/chatWebSocket';  
  30.     if ('WebSocket' in window) {  
  31.         Chat.socket = new WebSocket(wsURL);  
  32.     } else if ('MozWebSocket' in window) {  
  33.         Chat.socket = new MozWebSocket(wsURL);  
  34.     } else {  
  35.         alert("浏览器不支持");  
  36.         return false;  
  37.     }  
  38.   
  39.     Chat.socket.onopen = function() {  
  40.     };  
  41.   
  42.     Chat.socket.onclose = function() {  
  43.         Chat.writeToConsole("断开连接了 ");  
  44.         initChat();  
  45.     };  
  46.     Chat.socket.onmessage = function(message) {  
  47.         if (typeof message.data == "string") {// 如果发送的是字符串信息.  
  48.             var msgObj = eval("(" + message.data + ")");  
  49.             switch (msgObj.MsgType) {  
  50.                 case MsgTypeConstants.GET_USER_LIST :// 所有用户信息  
  51.                     Chat.preUserList(msgObj.userList);  
  52.                     break;  
  53.                 case MsgTypeConstants.SEND_ONE_TO_ALL :  
  54.                     Chat.writeToConsole(msgObj.msgContext);  
  55.                     break;  
  56.                 default :  
  57.                     alert("未知错误,请刷新页面");  
  58.             }  
  59.   
  60.         }  
  61.   
  62.     };  
  63.   
  64.     Chat.sendMessage = function() {  
  65.         Chat.socket.send(ueditor.getContentTxt());  
  66.     };  
  67. }  
  68.   
  69. Chat.writeToConsole = function(message) {  
  70. <span style="white-space: pre;">    </span>//往控制台打印得到的聊天消息  
  71. };  
  72.   
  73. /** 
  74.  * 处理刷新用户信息的方法。 
  75.  */  
  76. Chat.preUserList = function(userList) {  
  77.     //用户信息列表  
  78.   
  79. };  

 这些代码只是参考内容,实际上不可能拷贝下来直接运行,

如果有什么不理解的地方可以参看,有什么不对希望指出。有什么疑问希望提出。

分享到:
评论

相关推荐

    tomcat8.5.59

    Tomcat是Apache软件基金会下的一个开源项目,是一款广泛使用的Java Servlet容器,它实现了Java EE的Web应用程序规范。在本文中,我们将详细探讨Tomcat 8.5.59版本,这是Tomcat 8.x系列的一个稳定版本,具有诸多改进...

    jakarta.websocket-api-1.1.2-API文档-中英对照版.zip

    赠送jar包:jakarta.websocket-api-1.1.2.jar; 赠送原API文档:jakarta.websocket-api-1.1.2-javadoc.jar; 赠送源代码:jakarta.websocket-api-1.1.2-sources.jar; 赠送Maven依赖信息文件:jakarta.websocket-api...

    windows安装版-tomcat8.5.57.zip

    1. **版本特性**:Tomcat 8.5.x系列是基于Java EE 7标准的,相比于之前的版本,它增加了对WebSocket、JSON-P、JAX-RS 2.0等新特性的支持。 2. **配置**:在Windows环境下,可以通过修改“conf/server.xml”文件来...

    javax.websocket-api-1.1-API文档-中文版.zip

    赠送jar包:javax.websocket-api-1.1.jar; 赠送原API文档:javax.websocket-api-1.1-javadoc.jar; 赠送源代码:javax.websocket-api-1.1-sources.jar; 赠送Maven依赖信息文件:javax.websocket-api-1.1.pom; ...

    jakarta.websocket-api-1.1.2-API文档-中文版.zip

    赠送jar包:jakarta.websocket-api-1.1.2.jar; 赠送原API文档:jakarta.websocket-api-1.1.2-javadoc.jar; 赠送源代码:jakarta.websocket-api-1.1.2-sources.jar; 赠送Maven依赖信息文件:jakarta.websocket-api...

    tomcat-embed-websocket-8.5.15.jar

    tomcat-embed-websocket-8.5.15.jar

    node.js websocket

    const wss = new WebSocket.Server({ port: 8080 }); wss.on('connection', (ws) =&gt; { console.log('Client connected'); ws.on('message', (message) =&gt; { console.log(`Received message =&gt; ${message}`); ...

    tomcat-embed-websocket-9.0.16.jar

    tomcat-embed-websocket-9.0.16.jar

    node.js websocket socket.io unity 同步

    node.js websocket socket.io unity 同步 使用Node.js+socket.io制作服务端,unity+socket.io for unity 制作客户端 ,验证位置同步Demo (Javascript代码可使用Webstorm等IDE进行编写)

    sockjs.js websocket api

    sockjs.js websocket api 亲测可用 if(this.websocket==null) { // 首先判断是否 支持 WebSocket if ('WebSocket' in window) { this.websocket = new WebSocket( "ws://"+this.vbshopURL+"/layim/server?id="+...

    minchat:基于tomcat 7. 0.56 websocket的在线客服系统

    民聊基于tomcat 7. 0.56 websocket的在线客服系统## WebSocket介绍WebSocket协议是一种双向通信协议,它建立在TCP之上,同http一样通过TCP来传输数据,但是它和http最大的不同有两点:1.WebSocket是一种双向通信协议...

    javax.websocket-api-1.1

    在使用`javax.websocket-api-1.1`时,开发人员还需要一个支持WebSocket的服务器环境,如Tomcat、Jetty等,因为WebSocket API本身并不包含服务器实现。同时,为了在生产环境中部署,可能还需要考虑安全性、负载均衡、...

    javax.websocket-api-1.0.jar.zip

    "javax.websocket-api-1.0.jar"是Java WebSocket API的一个实现,它是Java EE 7规范的一部分,允许开发者在服务器端和客户端之间建立WebSocket连接。 WebSocket API的核心接口包括`ServerEndpoint`、`...

    Java后端WebSocket的Tomcat实现.docx

    2. **创建WebSocket服务端点**:创建一个继承自`javax.websocket.OnMessage`, `javax.websocket.OnOpen`, `javax.websocket.OnClose`, `javax.websocket.Session`等接口的类,这些接口定义了WebSocket连接的生命周期...

    apache-tomcat-10.0.8.zip

    Apache Tomcat 软件是Jakarta Servlet、 Jakarta Server Pages、 Jakarta Expression Language、 Jakarta WebSocket、 Jakarta Annotations和 Jakarta Authentication 规范的开源实现 。 压缩包内容: apache-...

    tomcat-7.0.108.zip

    12. **WebSocket支持**:Tomcat 7开始支持WebSocket协议,允许双向通信,为实时Web应用提供了基础。 以上就是关于Apache Tomcat 7.0.108的一些核心知识点。理解并掌握这些内容,有助于在实际开发和运维过程中更好地...

    javax.websocket-api-1.0.zip

    操作websocket时用到的javax.websocket包,可以引用import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax....

    javax.websocket-api-1.0.jar

    javaee7 标准websocket api

    最新版windows apache-tomcat-10.0.27-windows-x64.zip

    3. **Java WebSocket支持**:自Tomcat 7开始,它支持WebSocket协议,允许双向通信,提供实时应用功能。 4. **连接器技术**:Tomcat使用Coyote连接器处理网络通信,优化了I/O性能。 5. **部署管理**:可以通过管理...

    tomcat9.0.39压包.zip 内附压缩版zip和直装版exe

    tomcat9.0.39压包.zip 内附压缩版zip和直装版exe The Apache Tomcat® software is an open source implementation of the ...Servlet, JavaServer Pages, Java Expression Language and Java WebSocket technologies.

Global site tag (gtag.js) - Google Analytics