初識websocket

一馏段、定義


websocket.jpg

WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議姨伟。它實現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動發(fā)送信息給客戶端舷暮。
WebSocket協(xié)議支持(在受控環(huán)境中運行不受信任的代碼的)客戶端與(選擇加入該代碼的通信的)遠(yuǎn)程主機之間進(jìn)行全雙工通信屈呕。
二沟饥、產(chǎn)生的背景
簡單的說迅涮,WebSocket協(xié)議之前废赞,雙工通信是通過不停發(fā)送HTTP請求,從服務(wù)器拉取更新來實現(xiàn)逗柴,這導(dǎo)致了效率低下蛹头。WebSocket解決了這個問題
三、解決的問題
1.服務(wù)器被迫為每個客戶端使用許多不同的底層TCP連接:一個用于向客戶端發(fā)送信息戏溺,其它用于接收每個傳入消息渣蜗。
2.有些協(xié)議有很高的開銷,每一個客戶端和服務(wù)器之間都有HTTP頭旷祸。
3.客戶端腳本被迫維護(hù)從傳出連接到傳入連接的映射來追蹤回復(fù)耕拷。
四、實現(xiàn)原理和好處
1托享、原理:在實現(xiàn)websocket連線過程中骚烧,需要通過瀏覽器發(fā)出websocket連線請求,然后服務(wù)器發(fā)出回應(yīng)闰围,這個過程通常稱為“握手” 赃绊。
2、好處
① Header
互相溝通的Header是很小的-大概只有 2 Bytes
②Server Push
服務(wù)器的推送羡榴,服務(wù)器不再被動的接收到瀏覽器的請求之后才返回數(shù)據(jù)碧查,而是在有新數(shù)據(jù)時就主動推送給瀏覽器。

五校仑、請求響應(yīng)案例
瀏覽器請求
GET /webfin/websocket/ HTTP/1.1

Host: localhost
  Upgrade: websocket

Connection: Upgrade
  Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg==
  Origin: http://服務(wù)器地址
  Sec-WebSocket-Version: 13

服務(wù)器回應(yīng)
HTTP/1.1 101 Switching Protocols
  Upgrade: websocket
  Connection: Upgrade
  Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
WebSocket借用http請求進(jìn)行握手忠售,相比正常的http請求,多了一些內(nèi)容迄沫。
其中稻扬,Upgrade: websocket
Connection: Upgrade
表示希望將http協(xié)議升級到Websocket協(xié)議。
Sec-WebSocket-Key是瀏覽器隨機生成的base64 encode的值羊瘩,用來詢問服務(wù)器是否是支持WebSocket泰佳。

服務(wù)器返回
Upgrade: websocket
Connection: Upgrade
告訴瀏覽器即將升級的是Websocket協(xié)議
Sec-WebSocket-Accept是將請求包“Sec-WebSocket-Key”的值盼砍,與”258EAFA5-E914-47DA-95CA-C5AB0DC85B11″這個字符串進(jìn)行拼接,然后對拼接后的字符串進(jìn)行sha-1運算乐纸,再進(jìn)行base64編碼得到的衬廷。用來說明自己是WebSocket助理服務(wù)器。

六汽绢、個人demo
1.服務(wù)端
/*

  • websocket服務(wù)端

  • 客戶端向服務(wù)器端建立websocket的url

  • */
    @ServerEndpoint("/websocket")
    @Component
    public class WebSocketServer {
    //計算當(dāng)前在線人數(shù)
    private static int onlineCount=0;
    //concurrent包的線程安全set吗跋,用來存放每個客戶端對應(yīng)的mywebsocket對象,必須
    private static CopyOnWriteArraySet<WebSocketServer>webSocketSet=new CopyOnWriteArraySet<>();
    //與某個客戶端的連接會話宁昭,需要通過它來給客戶端發(fā)送數(shù)據(jù)跌宛,必須
    private Session session;

    /*

    • 連接建立成功使用的方法
    • /
      @OnOpen
      public void onOpen(Session session){
      this.session=session;
      webSocketSet.add(this);
      addOnlineCount(); //在線數(shù)加1
      System.out.println("有新窗口開始監(jiān)聽,當(dāng)前在線人數(shù)為" + getOnlineCount());
      try {
      sendMessage("連接成功");
      } catch (IOException e) {
      System.out.println("WebSocket IO異常");
      }
      }
      /

      連接關(guān)閉調(diào)用的方法
      /
      @OnClose
      public void onClose() {
      webSocketSet.remove(this); //從set中刪除
      subOnlineCount(); //在線數(shù)減1
      System.out.println("有連接關(guān)閉!當(dāng)前在線人數(shù)為" + getOnlineCount());
      }
      /
    • 收到客戶端消息后調(diào)用的方法
    • @param message 客戶端發(fā)送過來的消息
      /
      @OnMessage
      public void onMessage(String message, Session session) {
      System.out.println("收到客戶端的信息:" + message);
      //群發(fā)消息
      for (WebSocketServer item : webSocketSet) {
      try {
      item.sendMessage(message);
      } catch (IOException e) {
      e.printStackTrace();
      }
      }
      }
      /
      *
    • @param session
    • @param error
      */
      @OnError
      public void onError(Session session, Throwable error) {
      System.out.println("發(fā)生錯誤");
      error.printStackTrace();
      }

    /**

    • 實現(xiàn)服務(wù)器主動推送
      */
      public void sendMessage(String message) throws IOException {
      this.session.getBasicRemote().sendText(message);
      }
/**
 * 群發(fā)自定義消息
 */
public static void sendInfo(String message) throws IOException {
    System.out.println("推送消息內(nèi)容:" + message);
    for (WebSocketServer item : webSocketSet) {
        try {
            item.sendMessage(message);
        } catch (IOException e) {
            continue;
        }
    }
}

public static synchronized int getOnlineCount() {
    return onlineCount;
}

public static synchronized void addOnlineCount() {
    WebSocketServer.onlineCount++;
}

public static synchronized void subOnlineCount() {
    WebSocketServer.onlineCount--;
}

}
2.配置webSocket
/*

  • WEBSOCKET配置類

  • 開啟websocket支持

  • */
    @Configuration
    public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
    return new ServerEndpointExporter();
    }
    }
    3.提供接口
    @RestController
    @RequestMapping(value = "/socket")
    public class WebSocketController {
    //推送數(shù)據(jù)接口
    @RequestMapping("/push")
    public String pushMsg(HttpServletRequest request) {
    String message=request.getParameter("info");
    try {
    WebSocketServer.sendInfo(message);
    } catch (IOException e) {
    e.printStackTrace();
    }
    return "success";
    }
    }
    4.主類
    @SpringBootApplication
    public class WebsocketApplication {

    public static void main(String[] args) {
    SpringApplication.run(WebsocketApplication.class, args);
    }
    }
    5.前端客戶端
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>websocket頁面</title>
    <style>
    .ta1{
    width: 400px;
    height: 400px;
    overflow-x:hidden;
    background-color: tan
    }
    .ta2{
    width:400px;
    height:200px;
    overflow-x:hidden;
    background-color: aquamarine;
    }
    .one{
    width: 300px;
    height: 30px;
    border-radius: 5px;
    background-color: lightcoral
    }
    .two{
    width: 50px;
    height: 30px;
    background-color: cadetblue;
    border-radius: 5px;
    }
    body{
    text-align: center;
    }
    </style>
    </head>
    <body>
    <h2>WebSocket練習(xí)</h2>



    <script>
    function sendone() {
    socket.send(document.getElementById("userMsg").value);
    document.getElementById("userMsg").value=null;
    }
    var socket;
    if (typeof(WebSocket) == "undefined") {
    console.log("您的瀏覽器不支持WebSocket");
    } else {
    console.log("您的瀏覽器支持WebSocket");
    //實現(xiàn)化WebSocket對象积仗,指定要連接的服務(wù)器地址與端口建立連接
    socket = new WebSocket("ws://localhost:8080/websocket");

      //打開事件
      socket.onopen = function () {
          console.log("Socket已打開");
          //socket.send("這是來自客戶端的消息:" + new Date());
      };
    
      //獲得消息事件
      socket.onmessage = function (msg) {
          console.log(msg.data);
          alert(msg.data);
          document.getElementById("tao").value=document.getElementById("tao").value+'\n'+msg.data;
      };
    
      //關(guān)閉事件
      socket.onclose = function () {
          console.log("Socket已關(guān)閉");
      };
    
      //發(fā)生了錯誤事件
      socket.onerror = function () {
          alert("Socket發(fā)生了錯誤");
      }
    

    }
    </script>
    <h3>消息顯示區(qū)域</h3>
    <div>
    <textarea class="ta1" id="tao">

    </textarea>
    </div>
    <div>
    <input type="text" class="one" placeholder="輸入你要發(fā)送的信息" value="" id="userMsg">
    <input type="button" class="two" value="發(fā)送" onclick="sendone()">
    </div>



    <h3>服務(wù)器端廣播消息區(qū)域</h3>
    <div>
    <textarea class="ta2">

    </textarea>
    </div>
    </body>
    </html>
    6.前端服務(wù)器端
    <!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <title>server頁面</title>
    <style>
    textarea{
    width:200px;
    height: 30px;
    background-color: lightcoral;
    overflow-x:hidden;
    }
    .two{
    width: 50px;
    height: 30px;
    background-color: cadetblue;
    border-radius: 5px;
    }
    body{
    text-align: center;
    }
    </style>
    </head>
    <body>
    <h3>服務(wù)器推送消息</h3>

<form action="/socket/push" method="post" accept-charset="utf-8" name="loginfrom">
<textarea id="info" name="info"></textarea>
<input type="submit" class="two" value="發(fā)送">
</form>

</body

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末疆拘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子寂曹,更是在濱河造成了極大的恐慌哎迄,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隆圆,死亡現(xiàn)場離奇詭異漱挚,居然都是意外死亡,警方通過查閱死者的電腦和手機渺氧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進(jìn)店門旨涝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人侣背,你說我怎么就攤上這事白华。” “怎么了贩耐?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵弧腥,是天一觀的道長。 經(jīng)常有香客問我潮太,道長管搪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任消别,我火速辦了婚禮抛蚤,結(jié)果婚禮上台谢,老公的妹妹穿的比我還像新娘寻狂。我一直安慰自己,他們只是感情好朋沮,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布蛇券。 她就那樣靜靜地躺著缀壤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纠亚。 梳的紋絲不亂的頭發(fā)上塘慕,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天,我揣著相機與錄音蒂胞,去河邊找鬼图呢。 笑死,一個胖子當(dāng)著我的面吹牛骗随,可吹牛的內(nèi)容都是我干的蛤织。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鸿染,長吁一口氣:“原來是場噩夢啊……” “哼指蚜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起涨椒,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤摊鸡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蚕冬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體免猾,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年播瞳,在試婚紗的時候發(fā)現(xiàn)自己被綠了掸刊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡赢乓,死狀恐怖忧侧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牌芋,我是刑警寧澤蚓炬,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站躺屁,受9級特大地震影響肯夏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜犀暑,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一驯击、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧耐亏,春花似錦徊都、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽主之。三九已至,卻和暖如春李根,著一層夾襖步出監(jiān)牢的瞬間槽奕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工房轿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留粤攒,地道東北人。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓囱持,卻偏偏與公主長得像琼讽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子洪唐,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容