使用WebSocket實現服務端和客戶端的通訊

開發中常常會有這樣的使用場景.如某個用戶在一個數據上作了xx操做, 與該數據相關的用戶在線上的話,須要實時接收到一條信息.html

這種可使用WebSocket來實現. 另外,對於消息,能夠定義一個類進行固化. 主要是消息內容,接收人,發送人,是否已發送等. 用戶上線時, 經過方法去查詢出來而後進行發送前端

 

@ServerEndpoint(value = "/websocket/{sessionId}")
public class MyWebSocket {

//靜態變量,用來記錄當前在線鏈接數。應該把它設計成線程安全的。
private static AtomicInteger onlineCount = new AtomicInteger(0);

//concurrent包的線程安全Set,用來存放每一個客戶端對應的MyWebSocket對象。若要實現服務端與單一客戶端通訊的話,可使用Map來存放,其中Key能夠爲用戶標識
public static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();

//與某個客戶端的鏈接會話,須要經過它來給客戶端發送數據
public Session session;

/**
* 鏈接創建成功調用的方法
*
* @param session 可選的參數。session爲與某個客戶端的鏈接會話,須要經過它來給客戶端發送數據
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
if (webSocketSet.add(this)) {
System.out.println("有新鏈接加入!當前在線人數爲" + onlineCount.incrementAndGet());
}
}

/**
* 鏈接關閉調用的方法
*/
@OnClose
public void onClose() {
if (webSocketSet.remove(this)) {
System.out.println("有一鏈接關閉!當前在線人數爲" + onlineCount.decrementAndGet());
}
}

/**
* 收到客戶端消息後調用的方法
*
* @param message 客戶端發送過來的消息
* @param session 可選的參數
*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("來自客戶端的消息:" + message);
//羣發消息
/* for(MyWebSocket item: webSocketSet){
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}*/
}

/**
* 發生錯誤時調用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("發生錯誤");
error.printStackTrace();
}
private static ReentrantLock lock = new ReentrantLock(true);
/**
* 該方法是咱們根據業務須要調用的.
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
synchronized(this.session) {
if (session.isOpen()) {
this.session.getAsyncRemote().sendText(message);
}
}
}

}

 


頁面中的調用.每一個客戶都要初始化一個websocket示例. 其中咱們用用戶的userId做爲標識的一部分.web

//頁面加載完成. 初始化一個webSocket對象.而後能夠根據須要調一個來發信息
window.onload = function () {
initWebSocket();
setTimeout(function(){
$.post('<%=basePath %>xxx.do',function(r){
//alert(0);
});
},2000);
};


function initWebSocket() {
webSocket = new WebSocket(requestUrl.replace("http", "ws")
+ 'websocket/${userId}');

webSocket.onerror = function (event) {
onError(event)
};
//鏈接創建成功事件
webSocket.onopen = function (event) {
onOpen(event)
};
//接收到服務端消息事件
webSocket.onmessage = function (event) {
onMessage(event)
};
}

 


 
---------------------
做者:Wing_gor
來源:CSDN
原文:https://blog.csdn.net/qq_40085888/article/details/86308612ajax

----------------------------------------------------------------------------------------------------------npm

webSocket與html區別,以及服務端與客戶端消息通信利用webSocket

 

做者:Ovear
連接:https://www.zhihu.com/question/20215561/answer/40316953
來源:知乎
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

後端

1、WebSocket是HTML5出的東西(協議),也就是說HTTP協議沒有變化,或者說不要緊,但HTTP是不支持持久鏈接的(長鏈接,循環鏈接的不算)
首先HTTP有1.1和1.0之說,也就是所謂的keep-alive,把多個HTTP請求合併爲一個,可是Websocket實際上是一個新協議,跟HTTP協議基本沒有關係,只是爲了兼容現有瀏覽器的握手規範而已,也就是說它是HTTP協議上的一種補充能夠經過這樣一張圖理解
&amp;amp;lt;img src=&quot;https://pic1.zhimg.com/6651f2f811ec133b0e6d7e6d0e194b4c_b.jpg&quot; data-rawwidth=&quot;374&quot; data-rawheight=&quot;133&quot; class=&quot;content_image&quot; width=&quot;374&quot;&amp;amp;gt;有交集,可是並非所有。

有交集,可是並非所有。
另外Html5是指的一系列新的API,或者說新規範,新技術。Http協議自己只有1.0和1.1,並且跟Html自己沒有直接關係。。
通俗來講,你能夠用HTTP協議傳輸非Html數據,就是這樣=。=
再簡單來講,層級不同。瀏覽器

2、Websocket是什麼樣的協議,具體有什麼優勢
首先,Websocket是一個持久化的協議,相對於HTTP這種非持久的協議來講。
簡單的舉個例子吧,用目前應用比較普遍的PHP生命週期來解釋。
1) HTTP的生命週期經過Request來界定,也就是一個Request 一個Response,那麼在HTTP1.0中,此次HTTP請求就結束了。
在HTTP1.1中進行了改進,使得有一個keep-alive,也就是說,在一個HTTP鏈接中,能夠發送多個Request,接收多個Response。
可是請記住 Request = Response , 在HTTP中永遠是這樣,也就是說一個request只能有一個response。並且這個response也是被動的,不能主動發起。安全

教練,你BB了這麼多,跟Websocket有什麼關係呢?
_(:з」∠)_好吧,我正準備說Websocket呢。。
首先Websocket是基於HTTP協議的,或者說借用了HTTP的協議來完成一部分握手。
在握手階段是同樣的
-------如下涉及專業技術內容,不想看的能夠跳過lol:,或者只看加黑內容--------
首先咱們來看個典型的Websocket握手(借用Wikipedia的。。)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com
熟悉HTTP的童鞋可能發現了,這段相似HTTP協議的握手請求中,多了幾個東西。
我會順便講解下做用。
Upgrade: websocket
Connection: Upgrade
這個就是Websocket的核心了,告訴Apache、Nginx等服務器:注意啦,窩發起的是Websocket協議,快點幫我找到對應的助理處理~不是那個老土的HTTP。
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

首先,Sec-WebSocket-Key 是一個Base64 encode的值,這個是瀏覽器隨機生成的,告訴服務器:泥煤,不要忽悠窩,我要驗證尼是否是真的是Websocket助理。
而後,Sec_WebSocket-Protocol 是一個用戶定義的字符串,用來區分同URL下,不一樣的服務所須要的協議。簡單理解:今晚我要服務A,別搞錯啦~
最後,Sec-WebSocket-Version 是告訴服務器所使用的Websocket Draft(協議版本),在最初的時候,Websocket協議還在 Draft 階段,各類奇奇怪怪的協議都有,並且還有不少期奇奇怪怪不一樣的東西,什麼Firefox和Chrome用的不是一個版本之類的,當初Websocket協議太多但是一個大難題。。不過如今還好,已經定下來啦~你們都使用的一個東西~ 脫水:服務員,我要的是13歲的噢→_→服務器

而後服務器會返回下列東西,表示已經接受到請求, 成功創建Websocket啦!
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
這裏開始就是HTTP最後負責的區域了,告訴客戶,我已經成功切換協議啦~
Upgrade: websocket
Connection: Upgrade

依然是固定的,告訴客戶端即將升級的是Websocket協議,而不是mozillasocket,lurnarsocket或者shitsocket。
而後,Sec-WebSocket-Accept 這個則是通過服務器確認,而且加密事後的 Sec-WebSocket-Key。服務器:好啦好啦,知道啦,給你看個人ID CARD來證實行了吧。。
後面的,Sec-WebSocket-Protocol 則是表示最終使用的協議。websocket

至此,HTTP已經完成它全部工做了,接下來就是徹底按照Websocket協議進行了。
具體的協議就不在這闡述了。
------------------技術解析部分完畢------------------

&amp;amp;lt;img src=&quot;https://pic2.zhimg.com/afe119b52e096016139edabc2dfa9661_b.jpg&quot; data-rawwidth=&quot;161&quot; data-rawheight=&quot;187&quot; class=&quot;content_image&quot; width=&quot;161&quot;&amp;amp;gt;你TMD又BBB了這麼久,那到底Websocket有什麼鬼用,http long poll,或者ajax輪詢不均可以實現實時信息傳遞麼。你TMD又BBB了這麼久,那到底Websocket有什麼鬼用,http long poll,或者ajax輪詢不均可以實現實時信息傳遞麼。
&amp;amp;lt;img src=&quot;https://pic1.zhimg.com/20110e661edb1e93755a99c1d826e264_b.jpg&quot; data-rawwidth=&quot;176&quot; data-rawheight=&quot;193&quot; class=&quot;content_image&quot; width=&quot;176&quot;&amp;amp;gt;

好好好,年輕人,那咱們來說一講Websocket有什麼用。
來給你吃點胡(蘇)蘿(丹)卜(紅)
&amp;amp;lt;img src=&quot;https://pic4.zhimg.com/31ddf0cfbeecef21568d85ca60b5f1ff_b.jpg&quot; data-rawwidth=&quot;53&quot; data-rawheight=&quot;65&quot; class=&quot;content_image&quot; width=&quot;53&quot;&amp;amp;gt;

3、Websocket的做用
在講Websocket以前,我就順帶着講下 long poll 和 ajax輪詢 的原理。
首先是 ajax輪詢 ,ajax輪詢 的原理很是簡單,讓瀏覽器隔個幾秒就發送一次請求,詢問服務器是否有新信息。
場景再現:
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:沒有。。(Response)
客戶端:啦啦啦,有沒有新信息(Request)
服務端:你好煩啊,沒有啊。。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:好啦好啦,有啦給你。(Response)
客戶端:啦啦啦,有沒有新消息(Request)
服務端:。。。。。沒。。。。沒。。。沒有(Response) ---- loop

long poll 
long poll 其實原理跟 ajax輪詢 差很少,都是採用輪詢的方式,不過採起的是阻塞模型(一直打電話,沒收到就不掛電話),也就是說,客戶端發起鏈接後,若是沒消息,就一直不返回Response給客戶端。直到有消息才返回,返回完以後,客戶端再次創建鏈接,周而復始。
場景再現
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request)
服務端:額。。 等待到有消息的時候。。來 給你(Response)
客戶端:啦啦啦,有沒有新信息,沒有的話就等有了才返回給我吧(Request) -loop

從上面能夠看出其實這兩種方式,都是在不斷地創建HTTP鏈接,而後等待服務端處理,能夠體現HTTP協議的另一個特色,被動性。
何爲被動性呢,其實就是,服務端不能主動聯繫客戶端,只能有客戶端發起。
簡單地說就是,服務器是一個很懶的冰箱(這是個梗)(不會、不能主動發起鏈接),可是上司有命令,若是有客戶來,無論多麼累都要好好接待。

說完這個,咱們再來講一說上面的缺陷(原諒我廢話這麼多吧OAQ)
從上面很容易看出來,無論怎麼樣,上面這兩種都是很是消耗資源的。
ajax輪詢 須要服務器有很快的處理速度和資源。(速度)
long poll 須要有很高的併發,也就是說同時接待客戶的能力。(場地大小)
因此ajax輪詢 和long poll 都有可能發生這種狀況。

客戶端:啦啦啦啦,有新信息麼?
服務端:月線正忙,請稍後再試(503 Server Unavailable)
客戶端:。。。。好吧,啦啦啦,有新信息麼?
服務端:月線正忙,請稍後再試(503 Server Unavailable)

客戶端:&amp;amp;lt;img src=&quot;https://pic1.zhimg.com/7c0cf075c7ee4cc6cf52f4572a4c1c10_b.jpg&quot; data-rawwidth=&quot;143&quot; data-rawheight=&quot;50&quot; class=&quot;content_image&quot; width=&quot;143&quot;&amp;amp;gt;

而後服務端在一旁忙的要死:冰箱,我要更多的冰箱!更多。。更多。。(我錯了。。這又是梗。。)

--------------------------
言歸正傳,咱們來講Websocket吧
經過上面這個例子,咱們能夠看出,這兩種方式都不是最好的方式,須要不少資源。
一種須要更快的速度,一種須要更多的'電話'。這兩種都會致使'電話'的需求愈來愈高。
哦對了,忘記說了HTTP仍是一個無狀態協議。(感謝評論區的各位指出OAQ)
通俗的說就是,服務器由於天天要接待太多客戶了,是個健忘鬼,你一掛電話,他就把你的東西全忘光了,把你的東西全丟掉了。你第二次還得再告訴服務器一遍。

因此在這種狀況下出現了,Websocket出現了。
他解決了HTTP的這幾個難題。
首先,被動性,當服務器完成協議升級後(HTTP->Websocket),服務端就能夠主動推送信息給客戶端啦。
因此上面的情景能夠作以下修改。
客戶端:啦啦啦,我要創建Websocket協議,須要的服務:chat,Websocket協議版本:17(HTTP Request)
服務端:ok,確認,已升級爲Websocket協議(HTTP Protocols Switched)
客戶端:麻煩你有信息的時候推送給我噢。。
服務端:ok,有的時候會告訴你的。
服務端:balabalabalabala
服務端:balabalabalabala
服務端:哈哈哈哈哈啊哈哈哈哈
服務端:笑死我了哈哈哈哈哈哈哈

就變成了這樣,只須要通過一次HTTP請求,就能夠作到源源不斷的信息傳送了。(在程序設計中,這種設計叫作回調,即:你有信息了再來通知我,而不是我傻乎乎的每次跑來問你)
這樣的協議解決了上面同步有延遲,並且還很是消耗資源的這種狀況。
那麼爲何他會解決服務器上消耗資源的問題呢?
其實咱們所用的程序是要通過兩層代理的,即HTTP協議在Nginx等服務器的解析下,而後再傳送給相應的Handler(PHP等)來處理。
簡單地說,咱們有一個很是快速的接線員(Nginx),他負責把問題轉交給相應的客服(Handler)。
自己接線員基本上速度是足夠的,可是每次都卡在客服(Handler)了,老有客服處理速度太慢。,致使客服不夠。
Websocket就解決了這樣一個難題,創建後,能夠直接跟接線員創建持久鏈接,有信息的時候客服想辦法通知接線員,而後接線員在統一轉交給客戶。
這樣就能夠解決客服處理速度過慢的問題了。

同時,在傳統的方式上,要不斷的創建,關閉HTTP協議,因爲HTTP是非狀態性的,每次都要從新傳輸identity info(鑑別信息),來告訴服務端你是誰。
雖然接線員很快速,可是每次都要聽這麼一堆,效率也會有所降低的,同時還得不斷把這些信息轉交給客服,不但浪費客服的處理時間,並且還會在網路傳輸中消耗過多的流量/時間。
可是Websocket只須要一次HTTP握手,因此說整個通信過程是創建在一次鏈接/狀態中,也就避免了HTTP的非狀態性,服務端會一直知道你的信息,直到你關閉請求,這樣就解決了接線員要反覆解析HTTP協議,還要查看identity info的信息。
同時由客戶主動詢問,轉換爲服務器(推送)有信息的時候就發送(固然客戶端仍是等主動發送信息過來的。。),沒有信息的時候就交給接線員(Nginx),不須要佔用自己速度就慢的客服(Handler)了
--------------------
至於怎麼在不支持Websocket的客戶端上使用Websocket。。答案是:不能
可是能夠經過上面說的 long poll 和 ajax 輪詢來 模擬出相似的效果
-----
_(:з」∠)_兩天寫了兩篇科普類文章。。好累OAQ,求贊。。
對啦,若是有錯誤,歡迎你們在底下留言指出噢~
 
 
---------------------------------------------------------------------------------------------------------------------

WebSocket原理及如何使用

它有不少名字; WebSocket,WebSocket協議和WebSocket API。從首選的消息傳遞應用程序到流行的在線多人遊戲,WebSocket在當今最經常使用的Web應用程序中是相當重要的。

 

根據定義,WebSocket是經過單個TCP鏈接提供全雙工(雙向通訊)通訊信道的計算機通訊協議。此WebSocket API可在用戶的瀏覽器和服務器之間進行雙向通訊。用戶能夠向服務器發送消息並接收事件驅動的響應,而無需輪詢服務器。 它可讓多個用戶鏈接到同一個實時服務器,並經過API進行通訊並當即得到響應。

WebSockets容許用戶和服務器之間的流鏈接,並容許即時信息交換。在聊天應用程序的示例中,經過套接字聚集消息,能夠實時與一個或多個用戶交換,具體取決於誰在服務器上「監聽」(鏈接)。

 

WebSockets不只限於聊天/消息傳遞應用程序。它們適用於須要實時更新和即時信息交換的任何應用程序。一些示例包括但不限於:現場體育更新,股票行情,多人遊戲,聊天應用,社交媒體等等。

 

WebSockets還會考慮代理和防火牆等危險,使得任何鏈接均可以進行流式傳輸。它支持單個鏈接的上游和下游通訊。 它還減輕了服務器的負擔,容許現有機器支持同時鏈接。

 

這是現代Web應用程序中的示例實現WebSockets。在下面的示例中,我使用WebSockets做爲帶有Rails 5 API後端和React.js前端的即時消息應用程序。這毫不是一個指南,而是一個如何使用它的例子。我使用了Action Cable,它是Rails的包裝器,能夠無縫地集成Ruby中WebSockets的主要功能,並容許在整個域模型中輕鬆實現。 它內置於Rails 5.2中,所以無需安裝/執行任何外部庫或gem。

 

它的工做原理是Pub-Sub(發佈和訂閱)。它適用於發送者將數據(發佈者)發送給抽象數量的收件人(訂閱者),而無需指定他們是誰。

 

第一步是將服務器掛載到路由文件中,這樣前端就能夠從流中獲得endpoint:

在第5行,我設置了ActionCable服務器端點

 

下一步是在後端建立一個通道,以便在實時建立消息時對消息進行流式處理。

這是管理消息建立以及廣播消息的消息通道

 

這裏咱們有兩種方法,訂閱和接收。訂閱的第一種方法容許將消息通道流式傳輸到鏈接的用戶或訂戶。 接收的第二種方法管理消息建立和廣播消息。我實現了JWT用戶身份驗證,以即可以將消息追溯到用戶,只有具備有效用戶賬戶的消息才能建立消息。

 

對於個人應用程序的前端,我使用它們npm package actioncable從客戶端到服務器端鏈接到WebSocket API。 這個包直接來自於Rails的團隊。 使用此庫,我實例化了一個cableApp實例,並將其做爲props傳遞給須要訪問WebSocket鏈接的組件。

導入actionCable並建立了一個cableApp實例,而後將cableApp鏈接到個人後端端點「/ cable」並將其傳遞給須要鏈接的組件

 

而後,我經過React.js生命週期方法componentDidMount()鏈接到WebSocket的鏈接,並在每次將組件安裝到DOM時創建鏈接。

在componentDidMount()中,我創建了客戶端以鏈接到WebSocket協議,該協議從個人Rails API中的「messagesChannel」流式傳輸。

 

如今,每次建立消息並將其發送到接收方法時,訂閱(d)用戶將當即接收並可以實時查看消息。此實現支持訂閱同一頻道的多個用戶。所以,若是多個用戶簽名並訂閱了相同的頻道,他們能夠發送全部訂閱者都能看到的消息以及從其餘訂閱者接收消息。固然,你能夠限制爲兩我的,並使它成爲p2p,可是那樣還有什麼樂趣呢?

 

我但願經過閱讀本文,能夠看到WebSockets的潛力。它使本身成爲一個寶貴的資源,在這個時代,信息交換須要很快完成。 但願讀者將在本身的項目中實現它們。

譯者:張春

原文地址:

https://medium.com/@jamesjacobthomas7/websockets-and-how-i-used-them-a-quick-glance-37899b697852

相關文章
相關標籤/搜索