一、websocket與http長輪詢
websocket屬于html5 規范的一部分,提供的一種在單個 tcp 連接上進行全雙工通訊的協議。允許服務端主動向客戶端推送數據。在 websocket api 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。(tomcat 8以上版本支持)
http 協議是一種無狀態的、無連接的、單向的應用層協議。它采用了請求/響應模型。通信請求只能由客戶端發起,服務端對請求做出應答處理。這種通信模型有一個弊端:http 協議無法實現服務器主動向客戶端發起消息。
http長輪詢:客戶端向服務器發送(http)ajax請求,服務器接到請求后hold住連接,直到有新消息才返回響應信息并關閉連接,客戶端處理完響應信息后再向服務器發送新的請求。
websocket與http長輪詢的通信機制如下:
二、客戶端如何使用websocket
瀏覽器通過 javascript 向服務器發出建立 websocket 連接的請求,連接建立以后,客戶端和服務器端就可以通過 tcp 連接直接交換數據。
當你獲取 web socket 連接后,你可以通過send()方法來向服務器發送數據,并通過onmessage事件來接收服務器返回的數據。
1. 創建 websocket 對象api
1
2
3
|
//第一個參數 url, 指定連接的 url。第二個參數 protocol 是可選的,指定了可接受的子協議。 api:var websocket = new websocket(url, [protocol] ); |
2. websocket 屬性
以下是 websocket 對象的屬性。假定我們使用了以上代碼創建了 socket 對象:
屬性 | 描述 |
---|---|
socket.readystate |
只讀屬性 readystate 表示連接狀態,可以是以下值:
|
socket.bufferedamount |
只讀屬性 bufferedamount 已被 send() 放入正在隊列中等待傳輸,但是還沒有發出的 utf-8 文本字節數。 |
3. websocket 事件
以下是 websocket 對象的相關事件。假定我們使用了以上代碼創建了 socket 對象:
事件 | 事件處理程序 | 描述 |
---|---|---|
open | socket.onopen | 連接建立時觸發 |
message | socket.onmessage | 客戶端接收服務端數據時觸發 |
error | socket.onerror | 通信發生錯誤時觸發 |
close | socket.onclose | 連接關閉時觸發 |
4. websocket 方法
以下是 websocket 對象的相關方法。假定我們使用了以上代碼創建了 socket 對象:
方法 | 描述 |
---|---|
socket.send() |
使用連接發送數據 |
socket.close() |
關閉連接 |
5. websocket 實例
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
<%@ page language= "java" contenttype= "text/html; charset=utf-8" pageencoding= "utf-8" %> <html> <head> <script type= "text/javascript" src= "../js/jquery.min.js" ></script> <meta http-equiv= "content-type" content= "text/html; charset=utf-8" > <title>testing websocket</title> </head> <script type= "text/javascript" > var websocket; $(document).ready(function() { //var ip = window.location.host; var url = "ws://your ip/demo/ws/websocket" ; //判斷當前瀏覽器是否支持websocket if ( 'websocket' in window) { //建立客戶端websocket websocket = new websocket(url); } else { console.log( "您的瀏覽器不支持websocket" ); } //封裝內部監聽事件 function init(){ //連接發生錯誤的回調方法 websocket.onerror = function(event) { console.log( "websocket連接發生錯誤." ); }; //連接成功建立的回調方法 websocket.onopen = function(event) { console.log( "websocket連接成功." ); //心跳檢測重置 heartcheck.start(); }; //接收到消息的回調方法 websocket.onmessage = function(event) { //拿到任何消息都說明當前連接是正常的 if (event.data== "&" ){ console.log( "收到心跳回復" +event.data); } else { console.log( "接收到消息=" +event.data); onmessage(event); } heartcheck.start(); }; //連接關閉的回調方法 // 斷線重連 websocket.onclose = function() { console.log( "websocket連接斷開" ); } //監聽窗口關閉事件,當窗口關閉時,主動去關閉websocket連接,防止連接還沒斷開就關閉窗口,server端會拋異常。 window.onbeforeunload = function() { websocket.close(); console.log( "窗口關閉,websocket斷開連接." ); } } function onmessage(event) { setmessageinnerhtml(event.data); } function posttoserver() { websocket.send(document.getelementbyid( "msg" ).value); console.log( "向服務器發送消息=" +document.getelementbyid( "msg" ).value); document.getelementbyid( "msg" ).value = "" ; } //將消息顯示在網頁上 function setmessageinnerhtml(innerhtml) { document.getelementbyid( 'message' ).innerhtml += innerhtml + '<br/>' ; } function onclose(){ websocket.close(); } }); //心跳檢測 var heartcheck = { timeout: 10000 , timeoutobj: null , servertimeoutobj: null , start: function(){ console.log( "start heartcheck" ); var self = this ; this .timeoutobj && cleartimeout( this .timeoutobj); this .servertimeoutobj && cleartimeout( this .servertimeoutobj); this .timeoutobj = settimeout(function(){ //這里發送一個心跳,后端收到后,返回一個心跳消息, console.log( '發送心跳&' ); websocket.send( "&" ); self.servertimeoutobj = settimeout(function() { console.log(websocket); console.log( "服務器心跳回復超時" ); websocket.close(); }, self.timeout); }, this .timeout) } } </script> <body> <textarea id= "message" readonly></textarea> <br /> <input id= "msg" type= "text" /> <button type= "submit" id= "sendbutton" onclick= "posttoserver()" >send!</button> <button type= "submit" id= "sendbutton" onclick= "onclose()" >end</button> <script type= "text/javascript" src= "../js/websocket.js" ></script> </body> </html> |
三、服務器端如何使用websocket
1. pom.xml文件引入對應的依賴包:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<!-- websocket 開始--> <dependency> <groupid>javax.websocket</groupid> <artifactid>javax.websocket-api</artifactid> <version> 1.1 </version> <scope>provided</scope> </dependency> <dependency> <groupid>javax</groupid> <artifactid>javaee-api</artifactid> <version> 7.0 </version> <scope>provided</scope> </dependency> <!-- websocket 結束--> |
2. spring配置文件配置websocket服務類
1
2
3
|
<!-- websocket --> <bean id= "websocketservice" class = "minyuantec.tvwall.service.impl.websocketserviceimpl" scope= "singleton" > </bean> |
3. struts2配置文件過濾websocket請求
1
2
|
<!-- 過濾掉ws請求 --> <constant name= "struts.action.excludepattern" value= "/ws/.*,ws://.*" ></constant> |
4. 定義websocket.java類
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
99
100
101
102
103
104
105
106
|
@serverendpoint ( "/ws/websocket" ) public class websocket { private static logger logger = logger.getlogger(websocket. class ); @suppresswarnings ( "static-access" ) private messagehandler messagehandler = getwebsocketservice().messagehandler; //靜態變量,用來記錄當前在線連接數。應該把它設計成線程安全的 public static int onlinecount = 0 ; //與某個客戶端的連接會話,需要通過它來給客戶端發送數據 public session session; /** * 連接建立成功調用的方法 * @param session 可選的參數。session為與某個客戶端的連接會話,需要通過它來給客戶端發送數據 */ @onopen public void onopen(session session){ this .session = session; websocketmaputil.put(session.getid(), this ); //存入map addonlinecount(); //在線數加1 messagehandler.onconnect(session.getid()); system.out.println( "有新連接加入!當前在線人數為" + getonlinecount()); logger.info( "有新連接加入!當前在線人數為" + getonlinecount()); } /** * 連接關閉調用的方法 */ @onclose public void onclose(){ websocketmaputil.remove(session.getid()); //從map中刪除 subonlinecount(); //在線數減1 messagehandler.ondisconnect(session.getid()); try { if (session != null ){ session.close(); } } catch (ioexception e) { e.printstacktrace(); } system.out.println( "有一連接關閉!當前在線人數為" + getonlinecount()); logger.info( "有一連接關閉!當前在線人數為" + getonlinecount()); } /** * 收到客戶端消息后調用的方法 * @param message 客戶端發送過來的消息 * @param session 可選的參數 */ @onmessage public string onmessage(string message, session session) { system.out.println( "來自客戶端的消息:" + message); logger.info( "來自客戶端的消息:" + message); if (message.equals( "&" )){ return "&" ; } else { messagehandler.onmessage(session.getid(),message); return "got your message (" + message + ")" ; } } /** * 發生錯誤時調用 * @param session * @param error */ @onerror public void onerror(session session, throwable error){ system.out.println( "發生錯誤" ); logger.info( "發生錯誤:" + error); error.printstacktrace(); } //單發消息 public void sendmessage(string message) throws ioexception{ //阻塞式(同步) //this.session.getbasicremote().sendtext(message); //非阻塞式(異步) this .session.getasyncremote().sendtext(message); } //群發消息 public void sendmessageall(string message) throws ioexception{ for (websocket websocket : websocketmaputil.getvalues()){ websocket.sendmessage(message); } } public static synchronized int getonlinecount() { return onlinecount; } public static synchronized void addonlinecount() { websocket.onlinecount++; } public static synchronized void subonlinecount() { websocket.onlinecount--; } public websocketserviceimpl getwebsocketservice(){ classpathxmlapplicationcontext classpathxmlapplicationcontext = new classpathxmlapplicationcontext( "classpath*:applicationcontext.xml" ); return (websocketserviceimpl) classpathxmlapplicationcontext.getbean( "websocketservice" ); } } |
5. 在websocketserviceimpl實現類里實現服務器推送消息的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@override public boolean sendmessage(string message) { logger.info( "發送給客戶端的websocket消息message==" +message); if (messagehandler == null ) { logger.info( "上層調用者沒有給messagehandler賦值,websocket消息無法發送到客戶端" ); return false ; } if (message == null || "" .equals(message)) { logger.info( "發送給客戶端的消息message為空" ); return false ; } try { for (websocket websocket : websocketmaputil.getvalues()){ websocket.sendmessage(message); logger.info( "給sessionid為" + websocket.session.getid() + "的客戶端發送消息:" +message+ "=成功" ); } return true ; } catch (ioexception e) { logger.info( "向所有客戶端發送消息:" +message+ "=失敗:" + e); e.printstacktrace(); } return false ; } |
其實,使用tomcat8開發websocket服務端非常簡單,主要有如下兩種方式:
1. 使用注解方式開發(上述示例代碼),被@serverendpoint修飾的java類即可作為websocket服務端。
2. 繼承endpint基類實現websocket服務端。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/bingyimeiling/p/10276801.html