websocket服務端開發


基於http請求以拉的方式去做服務器的推送,無論是實時性和有效字節都是差強人意的效果。

公司的im系統在與客戶端的交互上實際上借助了websocket來實現服務器與客戶端的事實消息推送,今天就來簡單了解下這個協議,並且自己實現對websocket的響應。

 

可以看到在理解了tcp和http之后,websocket的設計其實並不復雜,再最開始建立鏈接的時候客戶端實際上會進行一次http請求,只不過請求頭的內容有些特別,這里我們來看下:

GET /chat HTTP/1.1

Host: server.example.com

Upgrade: websocket

Connection: Upgrade

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

Origin: http://example.com

Sec-WebSocket-Protool: chat,superchat

Sec-WebSocket-Version:13

可以看到這個報文里包含了一些附加頭信息。其中附加頭信息"Upgrade: WebSocket" ,表明這是一個申請協議升級的http請求。"Sec-WebSocket-Key"是隨機的,服務端會用這些數據構造出

一個SHA-1的信息摘要,把"Sec-WebSocket-Key"加上一個魔幻字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"。使用SHA-1加密,然后進行BASE-64編碼,將結果作為"Sec-WebSocket-Accept"頭的值,返回給客戶端:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protool: chat

實現對websocket請求的響應:

public class WebsocketServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket=new ServerSocket(8080);
        Socket socket= serverSocket.accept();
        new Thread(new Handle(socket)).start();
    }
}
public class Handle implements Runnable{
    private Socket socket;

    Handle(Socket socket){
        this.socket=socket;
    }

    @Override
    public void run() {
        try {
            BufferedReader reader=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            OutputStreamWriter streamWriter=new OutputStreamWriter(socket.getOutputStream());
            BufferedWriter bufferedWriter=new BufferedWriter(streamWriter);
            String key=null;
            //讀報文
            while (true){
               String s= reader.readLine();
               System.out.println(s);
                if (s.equals("")){
                   break;
                }else{
                    if (s.contains("Sec-WebSocket-Key")){
                        String keyValue[]=s.split(":");
                        key=keyValue[1].trim()+"258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
                    }
                }
            }
            //寫報文
            MessageDigest messageDigest=DigestUtils.getSha1Digest();
            byte[] digest=messageDigest.digest(key.getBytes());
            Base64 base64 = new Base64();
            String finalKey=base64.encodeToString(digest);
            bufferedWriter.write("HTTP/1.1 101 Switching Protocols\r\n");
            bufferedWriter.write("Upgrade: websocket\r\n");
            bufferedWriter.write("Connection: Upgrade\r\n");
            bufferedWriter.write("Sec-WebSocket-Accept: "+finalKey+"\r\n");
            bufferedWriter.write("Sec-WebSocket-Protool: chat\r\n");
            bufferedWriter.write("\r\n");
            bufferedWriter.write("test");
            bufferedWriter.write("\r\n");
            bufferedWriter.flush();
            //接收數據
            System.out.println("響應報文已經發送");
            while (true){
                String s=reader.readLine();
                System.out.println(s==null);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

 建立連接的時候要注意 Sec-WebSocket-Key在做sha-1哈希時,取得時摘要 然后拿着摘要去做base64遍嘛得到Sec-WebSocket-Accept,上面代碼還有問題就是連接建立之后 BufferedReader的readLine()方法在遇到\r,\r\n,\n之前會等到填滿緩沖區才會被喚醒,找的很多測試用的客戶端都會把換行符去掉導致線程在填滿緩沖區8KB之前一直阻塞,另一個問題就是字符流的編碼問題。這里更多的關注連接建立過程,連接建立之后其實就是直接用tcp傳輸數據了,這里不多做贅述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM