一.websocket簡單介紹
隨著互聯網的發展,傳統的http協議已經很難滿足web應用日益復雜的需求了。近年來,隨著html5的誕生,websocket協議被提出,它實現了瀏覽器與服務器的全雙工通信,擴展了瀏覽器與服務端的通信功能,使服務端也能主動向客戶端發送數據。
我們知道,傳統的http協議是無狀態的,每次請求(request)都要由客戶端(如 瀏覽器)主動發起,服務端進行處理后返回response結果,而服務端很難主動向客戶端發送數據;這種客戶端是主動方,服務端是被動方的傳統web模式 對于信息變化不頻繁的web應用來說造成的麻煩較小,而對于涉及實時信息的web應用卻帶來了很大的不便,如帶有即時通信、實時數據、訂閱推送等功能的應 用。在websocket規范提出之前,開發人員若要實現這些實時性較強的功能,經常會使用折衷的解決方法: 輪詢(polling) 和 comet 技術。其實后者本質上也是一種輪詢,只不過有所改進。
輪詢是最原始的實現實時web應用的解決方案。輪詢技術要求客戶端以設定的時間間隔周期性地向服務端發送請求,頻繁地查詢是否有新的數據改動。明顯地,這種方法會導致過多不必要的請求,浪費流量和服務器資源。
comet技術 又可以分為 長輪詢 和 流技術 。 長輪詢 改進了上述的輪詢技術,減小了無用的請求。它會為某些數據設定過期時間,當數據過期后才會向服務端發送請求;這種機制適合數據的改動不是特別頻繁的情況。 流技術 通常是指客戶端使用一個隱藏的窗口與服務端建立一個http長連接,服務端會不斷更新連接狀態以保持http長連接存活;這樣的話,服務端就可以通過這條長連接主動將數據發送給客戶端;流技術在大并發環境下,可能會考驗到服務端的性能。
這兩種技術都是基于請求-應答模式,都不算是真正意義上的實時技術;它們的每一次請求、應答,都浪費了一定流量在相同的頭部信息上,并且開發復雜度也較大。
伴隨著html5推出的websocket,真正實現了web的實時通信,使b/s模式具備了c/s模式的實時通信能力。websocket的工作流程是這 樣的:瀏覽器通過javascript向服務端發出建立websocket連接的請求,在websocket連接建立成功后,客戶端和服務端就可以通過 tcp連接傳輸數據。因為websocket連接本質上是tcp連接,不需要每次傳輸都帶上重復的頭部數據,所以它的數據傳輸量比輪詢和comet技術小 了很多。本文不詳細地介紹websocket規范,主要介紹下websocket在java web中的實現。
javaee 7中出了jsr-356:java api for websocket規范。不少web容器,如tomcat,nginx,jetty等都支持websocket。tomcat從7.0.27開始支持 websocket,從7.0.47開始支持jsr-356,下面的demo代碼也是需要部署在 tomcat7.0.47 以上的版本才能運行。
客戶端(web主頁)代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
<%@ page language= "java" pageencoding= "utf-8" %> <!doctype html> <html> <head> <title>java后端websocket的tomcat實現</title> </head> <body> welcome<br/><input id= "text" type= "text" /> <button onclick= "send()" >發送消息</button> <hr/> <button onclick= "closewebsocket()" >關閉websocket連接</button> <hr/> <div id= "message" ></div> </body> <script type= "text/javascript" > var websocket = null ; //判斷當前瀏覽器是否支持websocket if ( 'websocket' in window) { websocket = new websocket( "ws://172.16.98.31:8080/websocket/websocket" ); } else { alert( '當前瀏覽器 not support websocket' ); } //連接發生錯誤的回調方法 websocket.onerror = function () { setmessageinnerhtml( "websocket連接發生錯誤" ); }; //連接成功建立的回調方法 websocket.onopen = function () { setmessageinnerhtml( "websocket連接成功" ); } //接收到消息的回調方法 websocket.onmessage = function (event) { setmessageinnerhtml(event.data); } //連接關閉的回調方法 websocket.onclose = function () { setmessageinnerhtml( "websocket連接關閉" ); } //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。 window.onbeforeunload = function () { closewebsocket(); } //將消息顯示在網頁上 function setmessageinnerhtml(innerhtml) { document.getelementbyid( 'message' ).innerhtml += innerhtml + '<br/>' ; } //關閉websocket連接 function closewebsocket() { websocket.close(); } //發送消息 function send() { var message = document.getelementbyid( 'text' ).value; websocket.send(message); } </script> </html> |
java web后端代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
package cn.com; import java.io.ioexception; import java.util.concurrent.copyonwritearrayset; import javax.websocket.*; import javax.websocket.server.serverendpoint; /** * @serverendpoint 注解是一個類層次的注解,它的功能主要是將目前的類定義成一個websocket服務器端, * 注解的值將被用于監聽用戶連接的終端訪問url地址,客戶端可以通過這個url來連接到websocket服務器端 * 每次請求,都會創建一個實例 */ @serverendpoint ( "/websocket" ) public class websockettest { //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的。 private static int onlinecount = 0 ; //concurrent包的線程安全set,用來存放每個客戶端對應的mywebsocket對象。若要實現服務端與單一客戶端通信的話,可以使用map來存放,其中key可以為用戶標識 private static copyonwritearrayset<websockettest> websocketset = new copyonwritearrayset<websockettest>(); //與某個客戶端的連接會話,需要通過它來給客戶端發送數據 private session session; /** * 連接建立成功調用的方法 * @param session 可選的參數。session為與某個客戶端的連接會話,需要通過它來給客戶端發送數據 */ @onopen public void onopen(session session){ this .session = session; websocketset.add( this ); //加入set中 addonlinecount(); //在線數加1 system.out.println( "有新連接加入!當前在線人數為" + getonlinecount()); } /** * 連接關閉調用的方法 */ @onclose public void onclose(){ websocketset.remove( this ); //從set中刪除 subonlinecount(); //在線數減1 system.out.println( "有一連接關閉!當前在線人數為" + getonlinecount()); } /** * 收到客戶端消息后調用的方法 * @param message 客戶端發送過來的消息 * @param session 可選的參數 */ @onmessage public void onmessage(string message, session session) { system.out.println( "來自客戶端的消息:" + message); //群發消息 for (websockettest 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(); } /** * 這個方法與上面幾個方法不一樣。沒有用注解,是根據自己需要添加的方法。 * @param message * @throws ioexception */ public void sendmessage(string message) throws ioexception{ this .session.getbasicremote().sendtext(message); //this.session.getasyncremote().sendtext(message); } public static synchronized int getonlinecount() { return onlinecount; } public static synchronized void addonlinecount() { websockettest.onlinecount++; } public static synchronized void subonlinecount() { websockettest.onlinecount--; } } |
打開兩個瀏覽器,輸入網址,直接運行
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/andy-alone/p/9154353.html