1. 程式人生 > >web 開發相關筆記 #04# WebSocket

web 開發相關筆記 #04# WebSocket

網站 hyperlink private reat nal size dia ever uno

本文的主要內容:

  1. HTTP VS. WebSocket
  2. WebSocket 的客戶端實現(JavaScript)
  3. WebSocket 的服務端實現(Java & apache WebSocketAPI)
  4. WebSocket 深入學習

HTTP VS. WebSocket

簡單來講, WebSocket 就是一種允許服務端主動向客戶端 PUSH 數據的技術。

傳統的 HTTP 只能是客戶端先主動向服務器發起請求然後獲得服務器響應。

WebSocket是HTML5開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。

在WebSocket API中,瀏覽器和服務器只需要做一個握手的動作,然後,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。

瀏覽器通過 JavaScript 向服務器發出建立 WebSocket 連接的請求,連接建立以後,客戶端和服務器端就可以通過 TCP 連接直接交換數據。

當你獲取 Web Socket 連接後,你可以通過 send() 方法來向服務器發送數據,並通過 onmessage 事件來接收服務器返回的數據。

上面是網絡上對於 websocket 的一段簡短介紹。

值得一提的是,有不少人認為 websocket (於2011年被IETF定為標準RFC 6455)是一項可以取代 AJAX (約 1998 年前後得到應用)的技術。

簡單的草圖(。。自己畫的不太極不嚴謹,例如說ws最開始有個 HTTP 驗證漏掉了。。但是可以表達大概意思吧):

技術分享圖片

然後是我收集的一些關於 websocket 的資料:

1、WebSocket - Wikipedia 比較權威、全面

2、An Introduction to WebSockets - Treehouse Blog 生動的個人博客

3、websocket.org - Powered by Kaazing 以 websocket 為主題的網站!(websocket.org - WebSocket technology, demos, articles, and products.)

4、菜鳥教程之 websocket 趕時間的看這個。

總而言之,websocket 非常強大,應用也非常多,例如說可以用來構建基於瀏覽器的網頁遊戲、即時聊天工具之類的。

順便收藏(主題無關 ~ ):

What‘s the difference between getRequestURI and getPathInfo methods in HttpServletRequest?

WebSocket 的客戶端實現(JavaScript)

客戶端代碼完全拷貝自上面列出來的資料二:

index.html

<!DOCTYPE html>
<html 
lang="en"> <head> <meta charset="utf-8"> <title>WebSockets Demo</title> <link rel="stylesheet" href="style.css"> </head> <body> <div id="page-wrapper"> <h1>WebSockets Demo</h1> <div id="status">Connecting...</div> <ul id="messages"></ul> <form id="message-form" action="#" method="post"> <textarea id="message" placeholder="Write your message here..." required></textarea> <button type="submit">Send Message</button> <button type="button" id="close">Close Connection</button> </form> </div> <script src="app.js"></script> </body> </html>

/

style.css

技術分享圖片
*, *:before, *:after {
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    box-sizing: border-box;
}

html {
    font-family: Helvetica, Arial, sans-serif;
    font-size: 100%;
    background: #333;
}

#page-wrapper {
    width: 650px;
    background: #FFF;
    padding: 1em;
    margin: 1em auto;
    border-top: 5px solid #69c773;
    box-shadow: 0 2px 10px rgba(0,0,0,0.8);
}

h1 {
    margin-top: 0;
}

#status {
    font-size: 0.9rem;
    margin-bottom: 1rem;
}

.open {
    color: green;
}

.closed {
    color: red;
}


ul {
    list-style: none;
    margin: 0;
    padding: 0;
    font-size: 0.95rem;
}

ul li {
    padding: 0.5rem 0.75rem;
    border-bottom: 1px solid #EEE;
}

ul li:first-child {
    border-top: 1px solid #EEE;
}

ul li span {
    display: inline-block;
    width: 90px;
    font-weight: bold;
    color: #999;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 1px;
}

.sent {
    background-color: #F7F7F7;
}

.received {}

#message-form {
    margin-top: 1.5rem;
}

textarea {
    width: 100%;
    padding: 0.5rem;
    font-size: 1rem;
    border: 1px solid #D9D9D9;
    border-radius: 3px;
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
    min-height: 100px;
    margin-bottom: 1rem;
}

button {
    display: inline-block;
    border-radius: 3px;
    border: none;
    font-size: 0.9rem;
    padding: 0.6rem 1em;
    color: white;
    margin: 0 0.25rem;
    text-align: center;
    background: #BABABA;
    border-bottom: 1px solid #999;
}

button[type="submit"] {
    background: #86b32d;
    border-bottom: 1px solid #5d7d1f;
}

button:hover {
    opacity: 0.75;
    cursor: pointer;
}
View Code

/

app.js

window.onload = function() {

    // Get references to elements on the page.
    var form = document.getElementById(‘message-form‘);
    var messageField = document.getElementById(‘message‘);
    var messagesList = document.getElementById(‘messages‘);
    var socketStatus = document.getElementById(‘status‘);
    var closeBtn = document.getElementById(‘close‘);

    // The rest of the code in this tutorial will go here...
    // Create a new WebSocket.
    var socket = new WebSocket(‘ws://echo.websocket.org‘);

    // Show a connected message when the WebSocket is opened.
    socket.onopen = function(event) {
        socketStatus.innerHTML = ‘Connected to: ‘ + event.currentTarget.url;
        socketStatus.className = ‘open‘;
    };

    // Handle any errors that occur.
    socket.onerror = function(error) {
        console.log(‘WebSocket Error: ‘ + error);
    };

    // Send a message when the form is submitted.
    form.onsubmit = function(e) {
        e.preventDefault();

        // Retrieve the message from the textarea.
        var message = messageField.value;

        // Send the message through the WebSocket.
        socket.send(message);

        // Add the message to the messages list.
        messagesList.innerHTML += ‘<li class="sent"><span>Sent:</span>‘ + message +
            ‘</li>‘;

        // Clear out the message field.
        messageField.value = ‘‘;

        return false;
    };

    // Handle messages sent by the server.
    socket.onmessage = function(event) {
        var message = event.data;
        messagesList.innerHTML += ‘<li class="received"><span>Received:</span>‘ +
            message + ‘</li>‘;
    };

    // Show a disconnected message when the WebSocket is closed.
    socket.onclose = function(event) {
        socketStatus.innerHTML = ‘Disconnected from WebSocket.‘;
        socketStatus.className = ‘closed‘;
    };

    // Close the WebSocket connection when the close button is clicked.
    closeBtn.onclick = function(e) {
        e.preventDefault();

        // Close the WebSocket.
        socket.close();

        return false;
    };
};

/

直接把 三個文件放在同一個目錄下,用瀏覽器打開 index.html 就可以用了。連接的是代碼原作者的 websocket 服務器哦!

WebSocket 的服務端實現(Java & apache WebSocketAPI)

構建WebSocket 服務器的方式有很多,不過我只會用 tomcat 的 API,代碼即是 Tomcat 的 websocket example 相比自己寫的肯定比較規範啦。↓

技術分享圖片

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.sample.web;

import java.io.IOException;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(value = "/websocket")
public class ChatAnnotation {

    private static final String GUEST_PREFIX = "Guest";
    private static final AtomicInteger connectionIds = new AtomicInteger(0);
    private static final Set<ChatAnnotation> connections =
            new CopyOnWriteArraySet<>();

    private final String nickname;
    private Session session;

    public ChatAnnotation() {
        nickname = GUEST_PREFIX + connectionIds.getAndIncrement();
    }


    @OnOpen
    public void start(Session session) {
        this.session = session;
        connections.add(this);
        String message = String.format("* %s %s", nickname, "has joined.");
        broadcast(message);
    }


    @OnClose
    public void end() {
        connections.remove(this);
        String message = String.format("* %s %s",
                nickname, "has disconnected.");
        broadcast(message);
    }


    @OnMessage
    public void incoming(String message) {
        // Never trust the client
        String filteredMessage = String.format("%s: %s",
                nickname, message.toString());
        broadcast(filteredMessage);
    }




    @OnError
    public void onError(Throwable t) throws Throwable {
    }


    private static void broadcast(String msg) {
        for (ChatAnnotation client : connections) {
            try {
                synchronized (client) {
                    client.session.getBasicRemote().sendText(msg);
                }
            } catch (IOException e) {
                connections.remove(client);
                try {
                    client.session.close();
                } catch (IOException e1) {
                    // Ignore
                }
                String message = String.format("* %s %s",
                        client.nickname, "has been disconnected.");
                broadcast(message);
            }
        }
    }
}

在運行之前需要修改 app.js 中的地址:

    // The rest of the code in this tutorial will go here...
    // Create a new WebSocket.
    var socket = new WebSocket(‘ws://localhost:8080/websocket‘);

WebSocket 深入學習

當然,上面僅用到了 WebSocket API 的一個小子集,要想真正用好這些 API 並沒那麽簡單。下面是我找到的一些學習資料(基本上看 URL 就知道什麽內容了):

1、https://docs.oracle.com/javaee/7/api/javax/websocket/package-summary.html

2、https://docs.oracle.com/javaee/7/tutorial/websocket.htm

3、http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html

4、https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_a_WebSocket_server_in_Java

5、http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/HomeWebsocket/WebsocketHome.html

6、http://www.baeldung.com/java-websockets

7、https://tomcat.apache.org/tomcat-9.0-doc/websocketapi/index.html

8、https://benas.github.io/2016/02/21/using-the-java-api-for-webSocket-to-create-a-chat-server.html

web 開發相關筆記 #04# WebSocket