基于Tomcat7.0.29的WebSocket實現
看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); } }; }}</pre>然后是ws.html上的JavaScript腳本,這個還是照舊了。
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "; <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> </pre>在Tomcat7.0.29上執行成功。