1. 程式人生 > >WebSocket瀏覽器與伺服器之間的對話(用於訊息推送)

WebSocket瀏覽器與伺服器之間的對話(用於訊息推送)



1.如果加入資料庫後,A發訊息時客戶B未上線,服務端將會把訊息存在資料庫中,等客戶B上線後,在將訊息取出傳送給客戶B

2.服務端也可傳送訊息到任意客戶端上。

程式的執行效果截圖如下(在chrome,搜狗,firefox下測試通過):程式碼將在最後給出

首先我們開啟一個瀏覽器,顯示輸入您的名字,這裡我輸入soar

image

image

在開啟第二個瀏覽器,這裡我輸入bill

image

image

這是如果我傳送hello bill i am soar給bill,點選send

image

在另一個瀏覽器上就可以看到

image

Websocket

1.websocket是什麼?

WebSocket是為解決客戶端與服務端實時通訊而產生的技術。其本質是先通過HTTP/HTTPS協議進行握手後建立一個用於交換資料的TCP連線,

此後服務端與客戶端通過此TCP連線進行實時通訊。

2.websocket的優點

以前我們實現推送技術,用的都是輪詢,在特點的時間間隔有瀏覽器自動發出請求,將伺服器的訊息主動的拉回來,在這種情況下,我們需要不斷的向伺服器 傳送請求,然而HTTP request 的header是非常長的,裡面包含的資料可能只是一個很小的值,這樣會佔用很多的頻寬和伺服器資源。會佔用大量的頻寬和伺服器資源。

WebSocket API最偉大之處在於伺服器和客戶端可以在給定的時間範圍內的任意時刻,相互推送資訊。在建立連線之後,伺服器可以主動傳送資料給客戶端。

此外,伺服器與客戶端之間交換的標頭資訊很小。

WebSocket並不限於以Ajax(或XHR)方式通訊,因為Ajax技術需要客戶端發起請求,而WebSocket伺服器和客戶端可以彼此相互推送資訊;

關於ajax,comet,websocket的詳細介紹,和websocket報文的介紹,大家可以參看http://www.shaoqun.com/a/54588.aspx  網頁設計]Ajax、Comet與Websocket,

我如果以後有時間,也會寫出來的

3.如何使用websocket

客戶端

在支援WebSocket的瀏覽器中,在建立socket之後。可以通過onopen,onmessage,onclose即onerror四個事件實現對socket進行響應

一個簡單是示例

var ws = new WebSocket(“ws://localhost:8080”);
ws.onopen = function()
{
  console.log(“open”);
  ws.send(“hello”);
};
ws.onmessage = function(evt)
{
  console.log(evt.data)
};
ws.onclose = function(evt)
{
  console.log(“WebSocketClosed!”);
};
ws.onerror = function(evt)
{
  console.log(“WebSocketError!”);
};

1.var ws = new WebSocket(“ws://localhost:8080”);

申請一個WebSocket物件,引數是需要連線的伺服器端的地址,同http協議使用http://開頭一樣,WebSocket協議的URL使用ws://開頭,另外安全的WebSocket協議使用wss://開頭。

ws.send(“hello”);

用於叫訊息傳送到服務端

 

2.ws.onopen = function() { console.log(“open”)};

當websocket建立成功時,即會觸發onopen事件

 

3.ws.onmessage = function(evt) { console.log(evt.data) };

當客戶端收到服務端發來的訊息時,會觸發onmessage事件,引數evt.data中包含server傳輸過來的資料

 

4.ws.onclose = function(evt) { console.log(“WebSocketClosed!”); };

當客戶端收到服務端傳送的關閉連線的請求時,觸發onclose事件

 

5.ws.onerror = function(evt) { console.log(“WebSocketError!”); };

如果出現連線,處理,接收,傳送資料失敗的時候就會觸發onerror事件

我們可以看出所有的操作都是採用事件的方式觸發的,這樣就不會阻塞UI,使得UI有更快的響應時間,得到更好的使用者體驗。

 

服務端

現在有很多的伺服器軟體支援websocket,比如node.js,jetty,tomcat等

這裡我使用的是tomat 7.0和eclipse4.2

在tomcat下使用websocket首先需要匯入相關的jar

tomcat7提供的與WebSocket相關的類均位於包org.apache.catalina.websocket之中(包org.apache.catalina.websocket的實現包含於檔案catalina.jar之中

 

這裡我們把tomcat的全部匯入就行了

在build path->configure build path->librarise->add library->server runtime->apache tomcat v7.0

image

 

 

同時需要import以下包

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WsOutbound;
import org.apache.catalina.websocket.WebSocketServlet;

 

我們需要兩個類

第一個用於處理websocket請求

第二個用於處理每一次具體的WebSocket任務

 

第一個類


public class SocketServer extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    //……
    @Override
    protected StreamInbound createWebSocketInbound(String arg0,
            HttpServletRequest arg1) {
        // TODO Auto-generated method stub
        return new ChatWebSocket(users);
    }
}



這個Servlet繼承自WebSocketServlet,實現createWebSocketInbound方法。該方法返回第二個類的例項。

 

第二個類


public class ChatWebSocket extends MessageInbound {

        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {

        }

        @Override
        protected void onOpen(WsOutbound outbound) {
           
        }

        @Override
        protected void onClose(int status) {
           

        }

        @Override
        protected void onBinaryMessage(ByteBuffer arg0) throws IOException {

        }
//其餘略

    }



 

protected void onTextMessage(CharBuffer message) throws IOException { }

文字訊息響應

protected void onBinaryMessage(ByteBuffer arg0) throws IOException { }

二進位制訊息響應

protected void onOpen(WsOutbound outbound) { }

建立連線的觸發的事件

protected void onClose(int status) { }

關閉連線時觸發的事件

4.程式程式碼

html部分


<!DOCTYPE HTML>
< html>
< head>
< meta charset="utf-8">
< script type="text/javascript" src="js/jquery.js"></script>
< script type="text/javascript" src="js/socket.js"></script>
< title>無標題文件</title>
< /head>
< script language="javascript">

< /script>
< body>
< table>
  <tr>
    <td>Message</td>
    <td><input type="text" id="message"></td>
  </tr>
  <tr>
    <td>Name</td>
    <td><input type="text" id="othername"></td>
  </tr>
  <tr>
    <td><input id="sendbutton" type="button" value="send" onClick="click"  disabled="true">
      </input></td>
  </tr>
< /table>
< script>

< /script>
< /body>
< /html>



 

js部分(關於jquery部分不進行講解)


var username = window.prompt("輸入你的名字:");

document.write("Welcome<p id=\"username\">"+username+"</p>");

if (!window.WebSocket && window.MozWebSocket)
    window.WebSocket=window.MozWebSocket;
if (!window.WebSocket)
    alert("No Support ");
var ws;

$(document).ready(function(){
   
     $("#sendbutton").attr("disabled", false);
     $("#sendbutton").click(sendMessage);

    startWebSocket();
})

function sendMessage()
{
    var othername=$("#othername").val();
    var msg="MSG\t"+username+"_"+othername+"_"+$("#message").val();
    send(msg);
}
function send(data)
{
    console.log("Send:"+data);
    ws.send(data);
}
function startWebSocket()
{   
    ws = new WebSocket("ws://" + location.host + "/WebSocket/SocketServer");
    ws.onopen = function(){
        console.log("success open");
        $("#sendbutton").attr("disabled", false);
    };
     ws.onmessage = function(event)
     {
         console.log("RECEIVE:"+event.data);
         handleData(event.data);
     };
      ws.onclose = function(event) {
    console.log("Client notified socket has closed",event);
  };
 
}

function handleData(data)
{
    var vals=data.split("\t");
    var msgType=vals[0];
    switch(msgType)
    {
    case "NAME":
        var msg=vals[1];
        var mes="NAME"+"\t"+msg+"_"+ username;
        send(mes);
        break;
    case "MSG":
        var val2s=vals[1].split("_");
        var from=val2s[0];
        var message=val2s[2];
        alert(from+":"+message);
        break;
    default:
        break;
           
    }
}



java部分


import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;

import javax.servlet.http.HttpServletRequest;
import java.util.Set;


import java.util.concurrent.CopyOnWriteArraySet;

import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WsOutbound;
import org.apache.catalina.websocket.WebSocketServlet;



public class SocketServer extends WebSocketServlet {
    private static final long serialVersionUID = 1L;
    public final Set<ChatWebSocket> users = new CopyOnWriteArraySet<ChatWebSocket>();

    public static int USERNUMBER = 1;
    @Override
    protected StreamInbound createWebSocketInbound(String arg0,
            HttpServletRequest arg1) {
        // TODO Auto-generated method stub
        return new ChatWebSocket(users);
    }
    public class ChatWebSocket extends MessageInbound {

        private String username;
        private Set<ChatWebSocket> users = new CopyOnWriteArraySet<ChatWebSocket>();;

        public ChatWebSocket() {

        }

        public ChatWebSocket(Set<ChatWebSocket> users) {
            this.users = users;
        }

        @Override
        protected void onTextMessage(CharBuffer message) throws IOException {
            // 這裡處理的是文字資料
        }

        public void onMessage(String data) {
            String[] val1 = data.split("\\t");
            if(val1[0].equals("NAME"))
            {
                String[] val2=val1[1].split("_");
                for(ChatWebSocket user:users){
                    if (user.username.equals(val2[0])){
                        user.username=val2[1];
                    }
                }
            }
            else if(val1[0].equals("MSG"))
            {
                String[] val2=val1[1].split("_");
                for(ChatWebSocket user:users){
                    if (user.username.equals(val2[1])){
                        try {
                            CharBuffer temp=CharBuffer.wrap(data);
                            user.getWsOutbound().writeTextMessage(temp);
                        } catch (IOException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }
                }       
            }
            else
            {
                System.out.println("ERROR");
            }

        }

        @Override
        protected void onOpen(WsOutbound outbound) {
            // this.connection=connection;
            this.username = "#" + String.valueOf(USERNUMBER);
            USERNUMBER++;
            try {
                String message = "NAME" + "\t" + this.username;
                CharBuffer buffer = CharBuffer.wrap(message);
                this.getWsOutbound().writeTextMessage(buffer);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            users.add(this);
        }

        @Override
        protected void onClose(int status) {
            users.remove(this);

        }

        @Override
        protected void onBinaryMessage(ByteBuffer arg0) throws IOException {

        }

    }
   
}
 

解釋

這裡我的想法是

1 每個使用者在訪問的時候首先需要輸入自己的名字,接著向服務端傳送連線請求

2 服務端在接受到客戶端的連線請求後,會new ChatWebSocket(users);用於處理這個請求,並把它加入線上的使用者列表中,由於這個時候,服務端尚不知道客戶的名字。它會給這個使用者假定一個名字,#1,然後服務端會發送"NAME" + "\t" +“#1”給客戶端,你叫什麼?

3 客戶端收到這個訊息會知道,伺服器在問自己叫什麼名字,於是客戶端會發送"NAME"+"\t"+“#1”+"_"+ 自己的名字到服務端,(我叫xxx)

4 服務端收到這個訊息後根據#1在當前線上的使用者列表中進行查詢,將#1替換為客戶的名字,這樣服務端就知道了這個客戶的名字了

5 當客戶離開時,服務端會觸發onClose事件,服務端會把當前使用者從線上列表中移除

用圖畫出來類似這樣(畫的不好,—_—!!)

image

程式碼