websocket也是html5的新增加內容之一,號稱是下一代客戶端/服務器異步通信辦法,私以為雖然有點吹牛的成分,但是以后說不定能成為異步通信的半壁江山,至於取代ajax,我覺的應該不會。
websocket的一個很有意思的特點就是雙向通信,這一點其實也不稀奇,跟socket一樣的。
下邊是websocket的原理性知識總結是寫給我自己看的,如果你沒興趣,可以跳過直接到代碼:
tcp建立連接tcp連接的建立需要經歷”三次握手“的過程。過程如下client發送SYN包(值為j)以及SEQ包到server端,此時client進入SYN_SEND狀態。此為第一次握手。server端收到SYN包后,發送一個ACK(值為seq+1)確認包和SYN(值為k)給client,此時server進入SYN_RECV狀態。此為第二次握手。client收到SYN+ACK包后,向server發送一個ACK(值為k+1),該包發送完成后,client和server均進入ESTABLISH狀態。此為第三次握手。
client和server兩端狀態變化如下:
client:
CLOSED->SYN_SEND->ESTABLISH
server:
CLOSED->LISTEN->SYN_RECV->ESTABLISH
tcp是傳輸層的協議,tcp三次握手后,應用層協議http也便建立了連接。而對於當今web的發展情況,http仍有許多瓶頸。
一條連接只能發送一個請求。 請求只能從客戶端開始。客戶端不可以接收除響應以外的指令。 請求/響應首部未經壓縮發送,首部信息越多延遲越大。 發送冗長的首部。每次互相發送相同的首部造成較多的浪費。 可任意選擇數據壓縮格式。非強制壓縮發送。 雖然已經出現了很多解決方案,如ajax、comet,但是他們最終使用的都是http協議,因此也無法從根本上解決這些瓶頸。
因此也就誕生了一個新的通信協議,WebSocket協議,一種全雙工通信協議。
該通信協議建立在http協議的基礎之上,因此連接的發起方仍然是客戶端,在http連接建立之后,再將協議升級為webSocket連接,在webSocket連接建立之后,客戶度和服務器端都可以主動向對方發送報文信息了。
建立webSocket連接,需要先建立http連接,並在此基礎上再進行一次”握手“。
client會發起一個”握手“的請求,請求首部含有upgrade:websocket(還有其他首部,具體看如下示例)。服務器端返回一個101狀態碼,確認轉換協議。完成握手后便可以使用websocket協議進行通信。
Client(request)
GET /chat HTTP/1.1 Host: server.example.com Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: AQIDBAUGBwgJCgsMDQ4PEC== Origin: http://example.com Sec-WebSocket-protocol: chat, superchat Sec-WebSocket-Version: 13
server (response)
HTTP/1.1 101 switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Protocol: chat
websocket這個協議涉及前端顯示,以及服務器處理,我這里使用基礎的java+js來實現一個簡單的群聊

1 package example; 2 3 import java.io.IOException; 4 import java.util.Set; 5 import java.util.concurrent.CopyOnWriteArraySet; 6 7 import javax.websocket.OnClose; 8 import javax.websocket.OnError; 9 import javax.websocket.OnMessage; 10 import javax.websocket.OnOpen; 11 import javax.websocket.Session; 12 import javax.websocket.server.PathParam; 13 import javax.websocket.server.ServerEndpoint; 14 15 16 @ServerEndpoint(value="/ws/chat/{nickName}") 17 public class Chat { 18 private static final Set<Chat> connections = new CopyOnWriteArraySet<Chat>(); 19 private String nickName; //接收用戶名稱 20 private Session session; //建立的會話 21 22 23 public Chat(){ 24 25 } 26 27 /* 28 * 打開連接 29 */ 30 @OnOpen 31 public void onOpen(Session session,@PathParam(value="nickName") String nickName){ 32 this.session=session; 33 this.nickName=nickName; 34 connections.add(this); 35 System.out.println("新用戶連接進入,名字是:"+this.nickName); 36 String message=String.format("System>%s %s",this.nickName,"hasjoined."); 37 Chat.broadCast(message); 38 39 } 40 /* 41 * 關閉連接 42 */ 43 @OnClose 44 public void onClose(){ 45 connections.remove(this); 46 String message=String.format("System> %s, %s", this.nickName, 47 " has disconnection."); 48 Chat.broadCast(message); 49 } 50 51 /* 52 * 接收信息 53 */ 54 @OnMessage 55 public void onMessage(String message,@PathParam(value="nickName")String nickName){ 56 System.out.println("新消息from:"+nickName+" : "+message); 57 Chat.broadCast(nickName+">"+message); 58 } 59 /* 60 * 錯誤消息 61 */ 62 @OnError 63 public void onError(Throwable throwable){ 64 System.out.println(throwable.getMessage()); 65 } 66 /* 67 * 廣播消息 68 */ 69 private static void broadCast(String message){ 70 for(Chat chat:connections){ 71 try{ 72 synchronized (chat) { //線程同步控制並發訪問 73 chat.session.getBasicRemote().sendText(message); 74 } 75 }catch(IOException e){ 76 connections.remove(chat); 77 try{ 78 chat.session.close(); 79 80 }catch(IOException e1){ 81 chat.broadCast(String.format("System> %s %s", chat.nickName, 82 " has bean disconnection.")); 83 } 84 } 85 } 86 } 87 }
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8"/> 5 <title>Testing websockets</title> 6 </head> 7 <body> 8 <div> 9 <input type="text" id="yourName"/> 10 <button id="start">click to start</button> 11 </div> 12 <div style="width:100%;"> 13 <div id="messages" style="border:5px solid red;width:50%;height:300px;margin-top:20px;"></div> 14 </div> 15 <div style="width:100%;margin-top:20px;"> 16 <div id="sends" style="float:left;width:50%;height:50px;margin-right:20px;"> 17 <input type="text" id="MessageUN" style="width:100%;height:48px;"/> 18 </div> 19 <button style="float:left;width:10%;height:50px;" id="sendMessage">發送</button> 20 </div> 21 22 <script type="text/javascript"> 23 var button=document.getElementById("start"); 24 button.onclick=function(){ 25 26 27 28 29 var name=document.getElementById("yourName"); 30 console.log(name.value); 31 var websocketAdd="ws://localhost:8080/t8j8/ws/chat/"+name.value; 32 var webSocket=new WebSocket(websocketAdd); 33 34 35 function onMessage(event) { 36 document.getElementById('messages').innerHTML 37 += '<br />' + event.data; 38 } 39 40 function onOpen(event) { 41 document.getElementById('messages').innerHTML 42 = 'Connection established'; 43 //一旦鏈接開始,嘗試發出一條通訊消息 44 start(); 45 alert("消息通道開啟,可以發送消息了"); 46 //確認鏈接開始,就可以開始消息的發送了 47 //首先綁定一個點擊事件 48 var sendMessage=document.getElementById("sendMessage"); 49 sendMessage.onclick=function(){ 50 var message= document.getElementById('MessageUN').value; 51 //我們之前會有一個唯一的標識符,就是在click to start之前的標識符 52 webSocket.send(message); 53 //上邊一部完成之后,會自動觸發onmessage事件 54 } 55 56 } 57 58 59 function start() { 60 webSocket.send(name.value+" : "+'hello'); 61 } 62 63 function onError(event) { 64 alert(event.data); 65 } 66 67 webSocket.onerror = function(event) { 68 onError(event) 69 }; 70 71 webSocket.onopen = function(event) { 72 onOpen(event) 73 }; 74 75 webSocket.onmessage = function(event) { 76 onMessage(event) 77 }; 78 79 } 80 81 </script> 82 </body> 83 </html>
重要的只有兩個文件:chat.java以及chat.html,實現的是一個微型聊天室,當有用戶連接近來和發送消息,所有都能看到。