0
点赞
收藏
分享

微信扫一扫

WEBSOKET服务器搭建

小磊z 2022-12-27 阅读 196


简单介绍一下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代码  

WEBSOKET服务器搭建_WEBSOCKET



  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
  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
  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
  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


 

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

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

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

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

我们定义一个servlet

 


Java代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. @WebServlet(urlPatterns = { "/chatWebSocket"
  2. public class ChatWebSocketServlet extends
  3.   
  4. private static final long
  5.     OnLineUser theUser;  
  6.   
  7. @Override
  8. protected void
  9. throws
  10. "loginUser");  
  11. super.doGet(req, resp);  
  12.     }  
  13.   
  14. @Override
  15. protected
  16. return new
  17.     }  
  18.   
  19. }  


 

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


Java代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. @Override
  2. protected void
  3. throws
  4.   
  5. // Information required to send the server handshake message
  6.        String key;  
  7. 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. 426);  
  22. "Sec-WebSocket-Version", "13");  
  23. return;  
  24.        }  
  25.   
  26. "Sec-WebSocket-Key");  
  27. if (key == null) {  
  28.            resp.sendError(HttpServletResponse.SC_BAD_REQUEST);  
  29. return;  
  30.        }  
  31.   
  32. "Origin");  
  33. if
  34.            resp.sendError(HttpServletResponse.SC_FORBIDDEN);  
  35. return;  
  36.        }  
  37.   
  38.        List<String> subProtocols = getTokensFromHeader(req,  
  39. "Sec-WebSocket-Protocol-Client");  
  40. if
  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. "upgrade", "websocket");  
  53. "connection", "upgrade");  
  54. "Sec-WebSocket-Accept", getWebSocketAccept(key));  
  55. if (subProtocol != null) {  
  56. "Sec-WebSocket-Protocol", subProtocol);  
  57.        }  
  58. if
  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代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. @Override
  2. protected
  3. return new
  4.     }  


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


Java代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. public final class ChatMessageInbound extends
  2.   
  3. public
  4. this.theUser = theUser;  
  5.     }  
  6.   
  7. @Override
  8. protected void
  9. // 添加链接到容器
  10. this;  
  11.         ChatContainer.addInbound(theBound.theUser, theBound);  
  12. // 向每个在线用户发送消息
  13. new
  14. @Override
  15. public void
  16. new
  17.                 WriteTookit.writeToBound(theBound, listUserMsg.toMsg());  
  18.             }  
  19.         });  
  20.     }  
  21.   
  22. @Override
  23. protected void onClose(int
  24.         ChatContainer.removeInbound(theUser);  
  25.     }  
  26.   
  27. @Override
  28. protected void onBinaryMessage(ByteBuffer message) throws
  29.     }  
  30.   
  31. @Override
  32. protected void onTextMessage(CharBuffer message) throws
  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
  44. }  


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

下面是容器代码

 


Java代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. public final class
  2. /**
  3.      * 保存服务器连接的用户的容器
  4.      */
  5. private static final Map<OnLineUser, ChatMessageInbound> CHAT_MAP = new
  6.   
  7. /**
  8.      * 取出用户的连接
  9.      */
  10. public static
  11. return
  12.     }  
  13.   
  14. /**
  15.      * 放入一个连接
  16.      */
  17. public static void
  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
  30. return
  31.     }  
  32.   
  33. /**
  34.      * 遍历所有连接
  35.      */
  36. public static void
  37.         Iterator<OnLineUser> keyIter = CHAT_MAP.keySet().iterator();  
  38. while
  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
  50. void
  51.     }  
  52.   
  53. }  


 

 

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


Java代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. /**
  2.  * 前台和后台交互的信息类型常量
  3.  * 
  4.  * @author WangZhenChong
  5.  * 
  6.  */
  7. public final class
  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代码   ​​

WEBSOKET服务器搭建_WEBSOCKET

​​



  1. $(document).ready(function() {  
  2. "#connBtn").bind('click', function() {  
  3.         $.ajax({  
  4. "/tomcatWebSocket/Login#?asdasdasd",  
  5. "POST",  
  6. false,  
  7.             data : $.param({  
  8. "usernameField").value  
  9.                     }),  
  10. function(msg, status) {  
  11.                 initChat();  
  12.                 initUserList();  
  13. "#sendBtn").removeAttr("disabled");  
  14. "#connBtn").attr("disabled", "disabled");  
  15. "#usernameField").attr("disabled", "disabled");  
  16.             },  
  17. function(jqXHR, textStatus, errorThrown) {  
  18. "服务器内部错误");  
  19.             }  
  20.         });  
  21.   
  22.     });  
  23.       
  24.   
  25. var
  26. Chat.socket = null;  
  27. function
  28. var wsURL = 'ws://'
  29. '/tomcatWebSocket/chatWebSocket';  
  30. if ('WebSocket' in
  31. new
  32. else if ('MozWebSocket' in
  33. new
  34. else
  35. "浏览器不支持");  
  36. return false;  
  37.     }  
  38.   
  39. function() {  
  40.     };  
  41.   
  42. function() {  
  43. "断开连接了 ");  
  44.         initChat();  
  45.     };  
  46. function(message) {  
  47. if (typeof message.data == "string") {// 如果发送的是字符串信息.
  48. var msgObj = eval("(" + message.data + ")");  
  49. switch
  50. case MsgTypeConstants.GET_USER_LIST :// 所有用户信息
  51.                     Chat.preUserList(msgObj.userList);  
  52. break;  
  53. case
  54.                     Chat.writeToConsole(msgObj.msgContext);  
  55. break;  
  56. default
  57. "未知错误,请刷新页面");  
  58.             }  
  59.   
  60.         }  
  61.   
  62.     };  
  63.   
  64. 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. };  


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



举报

相关推荐

0 条评论