前几天研究了一下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类型。这个类型我们需要继承自己实现。打开源码观看这个类

有如下方法

  
/*** Intended to be overridden by sub-classes that wish to be notified* when the outbound connection is established. The default implementation* is a NO-OP.** @param outbound    The outbound WebSocket connection.*/protected void onOpen(WsOutbound outbound) {// NO-OP}/*** Intended to be overridden by sub-classes that wish to be notified* when the outbound connection is closed. The default implementation* is a NO-OP.** @param status    The status code of the close reason.*/protected void onClose(int status) {// NO-OP}/*** This method is called when there is a binary WebSocket message available* to process. The message is presented via a stream and may be formed from* one or more frames. The number of frames used to transmit the message is* not made visible to the application.** @param is    The WebSocket message** @throws IOException  If a problem occurs processing the message. Any*                      exception will trigger the closing of the WebSocket*                      connection.*/protected abstract void onBinaryData(InputStream is) throws IOException;/*** This method is called when there is a textual WebSocket message available* to process. The message is presented via a reader and may be formed from* one or more frames. The number of frames used to transmit the message is* not made visible to the application.** @param r     The WebSocket message** @throws IOException  If a problem occurs processing the message. Any*                      exception will trigger the closing of the WebSocket*                      connection.*/protected abstract void onTextData(Reader r) throws IOException;

  

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

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

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

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

我们定义一个servlet

@WebServlet(urlPatterns = { "/chatWebSocket" })
public class ChatWebSocketServlet extends WebSocketServlet {private static final long serialVersionUID = 1L;OnLineUser theUser;@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {theUser = (OnLineUser) req.getSession().getAttribute("loginUser");super.doGet(req, resp);}@Overrideprotected StreamInbound createWebSocketInbound(String subProtocol) {return new ChatMessageInbound(theUser);}}

  

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

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {// Information required to send the server handshake messageString key;String subProtocol = null;List<String> extensions = Collections.emptyList();if (!headerContainsToken(req, "upgrade", "websocket")) {resp.sendError(HttpServletResponse.SC_BAD_REQUEST);return;}if (!headerContainsToken(req, "connection", "upgrade")) {resp.sendError(HttpServletResponse.SC_BAD_REQUEST);return;}if (!headerContainsToken(req, "sec-websocket-version", "13")) {resp.setStatus(426);resp.setHeader("Sec-WebSocket-Version", "13");return;}key = req.getHeader("Sec-WebSocket-Key");if (key == null) {resp.sendError(HttpServletResponse.SC_BAD_REQUEST);return;}String origin = req.getHeader("Origin");if (!verifyOrigin(origin)) {resp.sendError(HttpServletResponse.SC_FORBIDDEN);return;}List<String> subProtocols = getTokensFromHeader(req,"Sec-WebSocket-Protocol-Client");if (!subProtocols.isEmpty()) {subProtocol = selectSubProtocol(subProtocols);}// TODO Read client handshake - Sec-WebSocket-Extensions// TODO Extensions require the ability to specify something (API TBD)//      that can be passed to the Tomcat internals and process extension//      data present when the frame is fragmented.// If we got this far, all is good. Accept the connection.resp.setHeader("upgrade", "websocket");resp.setHeader("connection", "upgrade");resp.setHeader("Sec-WebSocket-Accept", getWebSocketAccept(key));if (subProtocol != null) {resp.setHeader("Sec-WebSocket-Protocol", subProtocol);}if (!extensions.isEmpty()) {// TODO}// Small hack until the Servlet API provides a way to do this.StreamInbound inbound = createWebSocketInbound(subProtocol);((RequestFacade) req).doUpgrade(inbound);}

  

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

@Overrideprotected StreamInbound createWebSocketInbound(String subProtocol) {return new ChatMessageInbound(theUser);}

  

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

public final class ChatMessageInbound extends MessageInbound {public ChatMessageInbound(OnLineUser theUser) {this.theUser = theUser;}@Overrideprotected void onOpen(WsOutbound outbound) {// 添加链接到容器ChatMessageInbound theBound = this;ChatContainer.addInbound(theBound.theUser, theBound);// 向每个在线用户发送消息ChatContainer.eachAllBound(new ContainerCallBack() {@Overridepublic void eachCallBack(ChatMessageInbound theBound, OnLineUser theUser) {ListUserMsg listUserMsg = new ListUserMsg(ChatContainer.getUserList());WriteTookit.writeToBound(theBound, listUserMsg.toMsg());}});}@Overrideprotected void onClose(int status) {ChatContainer.removeInbound(theUser);}@Overrideprotected void onBinaryMessage(ByteBuffer message) throws IOException {}@Overrideprotected void onTextMessage(CharBuffer message) throws IOException {
//      CHAT_MODEL.setMessage(message.toString());
//      ChatContainer.eachAllBound(new ContainerCallBack() {
//          @Override
//          public void eachCallBack(ChatMessageInbound theBound, OnLineUser theUser) {
//              WriteTookit.writeToBound(theBound, CHAT_MODEL.getSayMsg());
//          }
//      });}// 变量区域private OnLineUser theUser;
}

  

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

下面是容器代码

public final class ChatContainer {/*** 保存服务器连接的用户的容器*/private static final Map<OnLineUser, ChatMessageInbound> CHAT_MAP = new HashMap<OnLineUser, ChatMessageInbound>();/*** 取出用户的连接*/public static ChatMessageInbound getInbound(OnLineUser theUser) {return CHAT_MAP.get(theUser);}/*** 放入一个连接*/public static void addInbound(OnLineUser theUser,ChatMessageInbound outbound) {CHAT_MAP.put(theUser, outbound);System.out.println(CHAT_MAP.size());}/*** 移除一个连接* * @param theUser* @return*/public static ChatMessageInbound removeInbound(OnLineUser theUser) {return CHAT_MAP.remove(theUser);}/*** 遍历所有连接*/public static void eachAllBound(ContainerCallBack callBackInter) {Iterator<OnLineUser> keyIter = CHAT_MAP.keySet().iterator();while (keyIter.hasNext()) {OnLineUser theUser = keyIter.next();callBackInter.eachCallBack(CHAT_MAP.get(theUser), theUser);}}/*** 回调函数的接口* * @author WangZhenChong*/public interface ContainerCallBack {void eachCallBack(ChatMessageInbound theBound, OnLineUser theUser);}}

  

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

/*** 前台和后台交互的信息类型常量* * @author WangZhenChong* */
public final class MsgTypeConstants {public static short GET_USER_LIST = 1;// 在线所有用户信息交互public static short SEND_ONE_TO_ONE = 2;// 对一个用户发送消息public static short SEND_ONE_TO_ALL = 3;// 对所有用户发送消息public static short SEND_SERVER_MSG = 4;// 发送系统消息
}

  

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

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

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

使用jquery

$(document).ready(function() {$("#connBtn").bind('click', function() {$.ajax({url : "/tomcatWebSocket/Login#?asdasdasd",type : "POST",processData : false,data : $.param({username : document.getElementById("usernameField").value}),success : function(msg, status) {initChat();initUserList();$("#sendBtn").removeAttr("disabled");$("#connBtn").attr("disabled", "disabled");$("#usernameField").attr("disabled", "disabled");},error : function(jqXHR, textStatus, errorThrown) {alert("服务器内部错误");}});});var Chat = {};
Chat.socket = null;
function initChat() {var wsURL = 'ws://' + window.location.host+ '/tomcatWebSocket/chatWebSocket';if ('WebSocket' in window) {Chat.socket = new WebSocket(wsURL);} else if ('MozWebSocket' in window) {Chat.socket = new MozWebSocket(wsURL);} else {alert("浏览器不支持");return false;}Chat.socket.onopen = function() {};Chat.socket.onclose = function() {Chat.writeToConsole("断开连接了 ");initChat();};Chat.socket.onmessage = function(message) {if (typeof message.data == "string") {// 如果发送的是字符串信息.var msgObj = eval("(" + message.data + ")");switch (msgObj.MsgType) {case MsgTypeConstants.GET_USER_LIST :// 所有用户信息Chat.preUserList(msgObj.userList);break;case MsgTypeConstants.SEND_ONE_TO_ALL :Chat.writeToConsole(msgObj.msgContext);break;default :alert("未知错误,请刷新页面");}}};Chat.sendMessage = function() {Chat.socket.send(ueditor.getContentTxt());};
}Chat.writeToConsole = function(message) {
//往控制台打印得到的聊天消息
};/*** 处理刷新用户信息的方法。*/
Chat.preUserList = function(userList) {//用户信息列表};

  

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

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

转载于:https://www.cnblogs.com/mrye/archive/2012/05/14/2499294.html

tomcat7.027-webSocket应用程序构建01相关推荐

  1. 使用 Apache Lucene 搜索文本——轻松为应用程序构建搜索和索引功能

    简介: 本文将探讨 Apache Lucene -- 性能卓越.功能全面的文本搜索引擎库.我们将学习 Lucene 架构及其核心 API.学习如何使用 Lucene 进行跨平台全文本搜索.建立索引.显 ...

  2. SCons — 程序构建工具

    目录 文章目录 目录 SCons - a software construction tool 使用示例 SCons - a software construction tool SCons 类似于 ...

  3. 程序员如何用糖果实现盈利 - [别人家的程序员01]

    程序员如何用糖果实现盈利 - [别人家的程序员01] 程序员如何用糖果实现盈利 - [别人家的程序员01] 前言 CandyJapan 网站如何从零走到今天 平台收支状况 如何做分析.写代码 总结 程 ...

  4. java添加容器_如何为Java应用程序构建docker容器

    我想要做的是为我的Java应用程序构建一个docker镜像,但对于大多数编译语言,以下注意事项应该是正确的. 问题 在我的构建服务器上,我想为我的应用程序生成一个docker镜像作为可交付成果.为此, ...

  5. 《Node应用程序构建——使用MongoDB和Backbone》一第 1 章 介绍与总览1.1 打造一个社交网络...

    本节书摘来自异步社区<Node应用程序构建--使用MongoDB和Backbone>一书中的第1章,第1.1节,作者[美]Mike Wilson,更多章节内容可以访问云栖社区"异 ...

  6. asp.net应用程序_如何在ASP.NET中为聊天应用程序构建键入指示器

    asp.net应用程序 by Neo Ighodaro 由新Ighodaro 如何在ASP.NET中为聊天应用程序构建键入指示器 (How to build a typing indicator fo ...

  7. webpack静态资源地址注入html,Webpack4+ 多入口程序构建

    欢迎关注富途web开发团队,缺人从众 其实,说实话这篇文章的由来也是有很多的原因在里面的.在这之前,我也做过不少的项目.有新的项目,也有旧的项目.通过对旧项目的不断研究,改进.再结合自己的理解,将新的 ...

  8. 小程序构建npm问题

    小程序构建npm问题 message:发生错误 Error: C:\Users\Administrator\Desktop\小程序\music\miniprogram\ 未找到 appid: wxf8 ...

  9. Android Graphics Tests 程序学习01

    转载自:http://zhoujinjian.cc/2018/12/20/Android%20Graphics%20Tests%20%E7%A8%8B%E5%BA%8F%E5%AD%A6%E4%B9% ...

最新文章

  1. 在有@Override方法上面会报错如下
  2. c# 带返回值的action_C#知识点讲解之C#delegate、event、Action、EventHandler的使用和区别...
  3. Servlet(自己实现的Servlet)细节
  4. Qt IFW基本用法
  5. java scanner_Java Scanner radix()方法与示例
  6. Python中的random库
  7. java web权限设计_java web简单权限管理设计
  8. 吴恩达深度学习笔记 3.1~3.11 浅层神经网络
  9. linux vim个人配色方案
  10. 【VS插件】VS2012设置透明背景
  11. 小明左、右手中分别拿两张纸牌:黑桃10和红桃8.现在交换手中的牌。
  12. 已经阻止语音服务器,关闭语音服务器
  13. 复习SSM day01 SSM Maven工程的搭建及配置文件
  14. 网络协议 11 - Socket 编程(下):眼见为实耳听为虚
  15. 景安虚拟主机 Typecho设置伪静态教程 Apache 环境
  16. Python报错不要慌,这三个关键词帮你解决问题!
  17. 计算机专业912,备战2019清华计算机912考研系列笔记绪论
  18. hosts屏蔽网站以及代理越过屏蔽
  19. 5.数据类型详解-字符串
  20. 禁止vite打包时将rgba转为16进制

热门文章

  1. php是实现点击改变状态,PHP利用header函数实现各种状态的跳转
  2. 常见的前端vue面试题
  3. java 垃圾回收 指针_Java 和 C++ 垃圾回收之间的差别
  4. mysql索引分析_MySQL索引分析和优化
  5. c++语言常量,Go语言常量和const关键字
  6. linux 磁盘查看sbli,Linux磁盘配额应用
  7. I - 查找练习 hash——出现过的数字(水题A的)
  8. 描述符:property 迭代器
  9. MFC中Mat实现打开本地图片
  10. 绘图: matplotlib核心剖析