日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tomcat7.027-webSocket应用程序构建01

發布時間:2025/3/21 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tomcat7.027-webSocket应用程序构建01 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前幾天研究了一下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的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。