基于Tomcat7.0.29的WebSocket实现

jopen 12年前

看HTML5的WebSocket的时候在Tomcat7.0.27上写WebSocket遇到了一个超时的问题,当时调试了一下Tomcat的源码,确认是底层使用的Socket设置了读超时的问题,只好放着不管,用Node.js搞了一个测试过去。

现在Tomcat版本换成7.0.29了,看Change Log,超时的问题貌似在7.0.28上解决了,重新把书上的echo.ws重新改成Tomcat版本的,功能很简单,就是网页上发什么文本,服务器就回什么文本。

53074: Switch to an infinite socket timeout by default for WebSocket connections. (markt)
服务器端程序需要用到关键是WebSocketServlet和MessageInbound,需要注意的是 WebSocketServlet 的createWebSocketInbound方法相对于7.0.27版本多了一个 HttpServletRequest参数(看文档上说的应该是7.0.28改的)
package fox.web.servlet;    import java.io.IOException;  import java.nio.ByteBuffer;  import java.nio.CharBuffer;  import java.util.logging.Logger;    import javax.servlet.annotation.WebServlet;  import javax.servlet.http.HttpServletRequest;    import org.apache.catalina.websocket.MessageInbound;  import org.apache.catalina.websocket.StreamInbound;  import org.apache.catalina.websocket.WebSocketServlet;  import org.apache.catalina.websocket.WsOutbound;    @WebServlet(urlPatterns = "/echo.ws")  // 处理WebSocket的Servlet需要继承自WebSocketServlet,这一点和7.0.27仍然一样  public class EchoServlet extends WebSocketServlet {   // Log   private Logger logger = Logger.getLogger(EchoServlet.class.getName());     @Override   // 与7.0.27不同的,Tomcat改变了createWebSocketInbound方法的定义,增加了一个HttpServletRequest参数   // 这样我们也可以从request参数中获取更多请求方的信息   protected StreamInbound createWebSocketInbound(String subProtocol,     HttpServletRequest request) {    // Log    logger.info("request ws servlet");      // 方法仍然是返回一个StreamInbound实例,这里采用实现他的子类MessageInbound    // 只用实现下面四个事件处理函数(其实onClose和onOpen有缺省实现)    return new MessageInbound() {     // WebSocket关闭事件,参数status应该来自org.apache.catalina.websocket.Constants中定义的几个常量,可以参考文档或者核对一下Tomcat的源码     @Override     protected void onClose(int status) {      // Log      logger.info("Web Socket Closed: " + status);     }       // WebSocket握手完成,创建完毕,WsOutbound用于向客户端发送数据     @Override     protected void onOpen(WsOutbound outbound) {      // Log      logger.info("Web Socket Open!");     }       // 有二进制消息数据到达,暂时没研究出这个函数什么情况下触发,js的WebSocket按理说应该只能send文本信息才对     @Override     protected void onBinaryMessage(ByteBuffer buffer)       throws IOException {      // Log      logger.info("Binary Message Receive: " + buffer.remaining());      // Nothing     }       // 有文本消息数据到达     @Override     protected void onTextMessage(CharBuffer buffer) throws IOException {      // Log      logger.info("Text Message Receive: " + buffer.remaining());      // getWsOutbound可以返回当前的WsOutbound,通过他向客户端回传数据,这里采用的是nio的CharBuffer      getWsOutbound().writeTextMessage(buffer);     }    };   }  }
然后是ws.html上的JavaScript脚本,这个还是照旧了。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">  <html>    <head>     <title>Web Socket Demo</title>     <script type="text/javascript">     var ws = null;       function startServer() {     // 设定WebSocket,注意协议是ws,请求是指向对应的WebSocketServlet的     var url = "ws://127.0.0.1:8080/j2ee6/echo.ws";     // 创建WebSocket实例,下面那个MozWebSocket是Firefox的实现     if ('WebSocket' in window) {       ws = new WebSocket(url);     } else if ('MozWebSocket' in window) {       ws = new MozWebSocket(url);      } else {       alert('Unsupported.');       return;      }       // WebSocket握手完成,连接成功的回调     // 有个疑问,按理说new WebSocket的时候就会开始连接了,如果在设置onopen以前连接成功,是否还会触发这个回调     ws.onopen = function() {       alert('Opened!');            };             // 收到服务器发送的文本消息, event.data表示文本内容     ws.onmessage = function(event) {       alert('Receive message: ' + event.data);      };       // 关闭WebSocket的回调     ws.onclose = function() {      alert('Closed!');      };    }      function sendMyMessage() {     var textMessage = document.getElementById('textMessage').value;          if(ws != null && textMessage != '') {      // 通过WebSocket想向服务器发送一个文本信息      ws.send(textMessage);     }    }    </script>   </head>   <body onload="startServer()">     <input type="text" id="textMessage" size="20" />    <input type="button" onclick="sendMyMessage()" value="Send">   </body>   </html> 
在Tomcat7.0.29上执行成功。