WebSocket簡單使用

一.Socket的概要

Socket又稱"套接字",應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求。Socket的英文原義是「孔」或「插座」,做爲UNIX的進程通訊機制。Socket能夠實現應用程序間網絡通訊。javascript

Socket可使用TCP/IP協議或UDP協議。html

TCP/IP協議前端

TCP/IP協議是目前應用最爲普遍的協議,是構成Internet國際互聯網協議的最爲基礎的協議,由TCP和IP協議組成:
TCP協議:面向鏈接的、可靠的、基於字節流的傳輸層通訊協議,負責數據的可靠性傳輸的問題。java

IP協議:用於報文交換網絡的一種面向數據的協議,主要負責給每臺網絡設備一個網絡地址,保證數據傳輸到正確的目的地。web

UDP協議後端

UDP特色:無鏈接、不可靠、基於報文的傳輸層協議,優勢是發送後不用管,速度比TCP快。瀏覽器

二.WebSocket的概要

B/S架構的系統多使用HTTP協議,HTTP協議的特色:安全

1 無狀態協議
2 用於經過 Internet 發送請求消息和響應消息
3 使用端口接收和發送消息,默認爲80端口
底層通訊仍是使用Socket完成。服務器

 

HTTP協議決定了服務器與客戶端之間的鏈接方式,沒法直接實現消息推送(F5已壞),一些變相的解決辦法:websocket

雙向通訊與消息推送

輪詢:客戶端定時向服務器發送Ajax請求,服務器接到請求後立刻返回響應信息並關閉鏈接。  優勢:後端程序編寫比較容易。  缺點:請求中有大半是無用,浪費帶寬和服務器資源。  實例:適於小型應用。

長輪詢:客戶端向服務器發送Ajax請求,服務器接到請求後hold住鏈接,直到有新消息才返回響應信息並關閉鏈接,客戶端處理完響應信息後再向服務器發送新的請求。  優勢:在無消息的狀況下不會頻繁的請求,耗費資小。  缺點:服務器hold鏈接會消耗資源,返回數據順序無保證,難於管理維護。 Comet異步的ashx, 實例:WebQQ、Hi網頁版、Facebook IM。

長鏈接:在頁面裏嵌入一個隱蔵iframe,將這個隱蔵iframe的src屬性設爲對一個長鏈接的請求或是採用xhr請求,服務器端就能源源不斷地往客戶端輸入數據。  優勢:消息即時到達,不發無用請求;管理起來也相對便。  缺點:服務器維護一個長鏈接會增長開銷。  實例:Gmail聊天

Flash Socket:在頁面中內嵌入一個使用了Socket類的 Flash 程序JavaScript經過調用此Flash程序提供的Socket接口與服務器端的Socket接口進行通訊,JavaScript在收到服務器端傳送的信息後控制頁面的顯示。  優勢:實現真正的即時通訊,而不是僞即時。  缺點:客戶端必須安裝Flash插件;非HTTP協議,沒法自動穿越防火牆。  實例:網絡互動遊戲。

Websocket:
WebSocket是HTML5開始提供的一種瀏覽器與服務器間進行全雙工通信的網絡技術。依靠這種技術能夠實現客戶端和服務器端的長鏈接,雙向實時通訊。
特色:
事件驅動
異步
使用ws或者wss協議的客戶端socket

可以實現真正意義上的推送功能

缺點:

少部分瀏覽器不支持,瀏覽器支持的程度與方式有區別。

 

WebSocket協議支持(在受控環境中運行不受信任的代碼的)客戶端與(選擇加入該代碼的通訊的)遠程主機之間進行全雙工通訊。用於此的安全模型是Web瀏覽器經常使用的基於原始的安全模式。 協議包括一個開放的握手以及隨後的TCP層上的消息幀。 該技術的目標是爲基於瀏覽器的、須要和服務器進行雙向通訊的(服務器不能依賴於打開多個HTTP鏈接(例如,使用XMLHttpRequest或<iframe>和長輪詢))應用程序提供一種通訊機制。
 

三.WebSocket實例

客戶端(Web主頁)代碼:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
  <head>
    <title>WebSocket前端</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://localhost:8080/websocket");
    }else{
        alert("當前瀏覽器 Not support websocket");
    }

    //鏈接發生錯誤的回調方法
    websocket.onerror=function(){
        setMessageInnerHTML("WebSocket鏈接發生錯誤");
    }

    //接收到消息的回調方法
    websocket.onmessage=function (event) {
        setMessageInnerHTML(event.data);
    }

    //鏈接關閉的回調方法
    websocket.onclose=function () {
        setMessageInnerHTML("WebSocket鏈接關閉");
    }

    //監聽窗口關閉時間,當窗口關閉時,主動去關閉websocket鏈接,防止鏈接還沒斷開就關閉窗口,server端會拋出異常。
    window.onbeforeprint=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>

後臺代碼:

package com;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;


@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--;
    }
}
相關文章
相關標籤/搜索