日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747


看完讓你徹底搞懂Websocket原理,附通過netty完成Websocket

 

 

(一)websocket協(xié)議概述

假設(shè)我們要實現(xiàn)一個WEB版的聊天室可以采用哪些方案?

1.Ajax輪詢?nèi)シ?wù)器取消息

客戶端按照某個時間間隔不斷地向服務(wù)端發(fā)送請求,請求服務(wù)端的最新數(shù)據(jù)然后更新客戶端顯示。這種方式實際上浪費了大量流量并且對服務(wù)端造成了很大壓力。

2.Flash XMLSocket

在 html 頁面中內(nèi)嵌入一個使用了 XMLSocket 類的 Flash 程序。JAVAScript 通過調(diào)用此Flash程序提供的套接口接口與服務(wù)器端的套接口進行通信。JavaScript 在收到服務(wù)器端以 XML 格式傳送的信

息后可以很容易地控制 HTML 頁面的內(nèi)容顯示。

  • 以上方案的弊端

Ajax 輪詢:

  1. Http為半雙工協(xié)議,也就是說同一時刻,只有一個方向的數(shù)據(jù)傳送。
  2. Http消息冗長,包含請求行、請求頭、請求體。占用很多的帶寬和服務(wù)器資源。
  3. 空輪詢問題。
  4. 政府項目直接用ajax,別搞那么復(fù)雜,它不存在并發(fā)問題。

Flash XMLSocket

  1. 客戶端必須安裝 Flash 播放器,而且瀏覽器需要授權(quán)。
  2. 因為 XMLSocket 沒有 HTTP 隧道功能,XMLSocket 類不能自動穿過防火墻。
  3. 因為是使用套接口,需要設(shè)置一個通信端口,防火墻、代理服務(wù)器也可能對非 HTTP 通道端口進行限制。

為了解決上述弊端,Html5定義了WebSocket協(xié)義能更好的節(jié)省服務(wù)器資源和寬帶達到實時通信的目的。

  • webSocket 協(xié)議簡介

webSocket 是html5 開始提供的一種瀏覽器與服務(wù)器間進行全雙工二進制通信協(xié)議,其基于TCP雙向全雙工作進行消息傳遞,同一時刻即可以發(fā)又可以接收消息,相比Http的半雙工協(xié)議性能有很大的提升。

  • webSocket特點如下:
  1. 單一TCP長連接,采用全雙工通信模式。
  2. 對代理、防火墻透明,80端口必須打開吧。
  3. 無頭部信息、消息更精簡。
  4. 通過ping/pong 來?;?。
  5. 服務(wù)器可以主動推送消息給客戶端,不在需要客戶輪詢。
  • WebSocket 協(xié)議報文格式

任何應(yīng)用協(xié)議都有其特有的報文格式,比如Http協(xié)議通過 空格 換行組成其報文。如http 協(xié)議不同在于WebSocket屬于二進制協(xié)議,通過規(guī)范進二進位來組成其報文。

 

看完讓你徹底搞懂Websocket原理,附通過netty完成Websocket

 

 

  • 報文說明:

FIN

標(biāo)識是否為此消息的最后一個數(shù)據(jù)包,占 1 bit

RSV1, RSV2, RSV3

用于擴展協(xié)議,一般為0,各占1bit

Opcode

數(shù)據(jù)包類型(frame type),占4bits

0x0:標(biāo)識一個中間數(shù)據(jù)包

0x1:標(biāo)識一個text類型數(shù)據(jù)包

0x2:標(biāo)識一個binary類型數(shù)據(jù)包

0x3-7:保留

0x8:標(biāo)識一個斷開連接類型數(shù)據(jù)包

0x9:標(biāo)識一個ping類型數(shù)據(jù)包

0xA:表示一個pong類型數(shù)據(jù)包

0xB-F:保留

MASK

占1bits

用于標(biāo)識PayloadData是否經(jīng)過掩碼處理。如果是1,Masking-key域的數(shù)據(jù)即是掩碼密鑰,用于解碼

PayloadData??蛻舳税l(fā)出的數(shù)據(jù)幀需要進行掩碼處理,所以此位是1。

Payload length

Payload data的長度,占7bits,7+16bits,7+64bits:

如果其值在0-125,則是payload的真實長度。如果值是126,則后面2個字節(jié)形成的16bits無符號整型數(shù)的值是payload的真實長度。注意,網(wǎng)絡(luò)字節(jié)序,需要轉(zhuǎn)換。如果值是127,則后面8個字節(jié)形成的64bits無符號整型數(shù)的值是payload的真實長度。注意,網(wǎng)絡(luò)字節(jié)序,需要轉(zhuǎn)換。

Payload data

應(yīng)用層數(shù)據(jù)

  • WebSocket 在瀏覽當(dāng)中的使用

Http 連接與webSocket 連接建立示意圖

 

看完讓你徹底搞懂Websocket原理,附通過netty完成Websocket

 

 

通過javaScript 中的API可以直接操作WebSocket 對象

var ws = new WebSocket(“ws://localhost:8080”);
ws.onopen = function()// 建?成功之后觸發(fā)的事件
{
 console.log(“打開連接”);
 ws.send("ddd"); // 發(fā)送消息
};
ws.onmessage = function(evt) { // 接收服務(wù)器消息
 console.log(evt.data);
};
ws.onclose = function(evt) {
 console.log(“WebSocketClosed!”); // 關(guān)閉連接
};
ws.onerror = function(evt) {
 console.log(“WebSocketError!”); // 連接異常
};

1.申請一個WebSocket對象,并傳入WebSocket地址信息,這時client會通過Http先發(fā)起握手請求

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket //告訴服務(wù)端需要將通信協(xié)議升級到websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== //瀏覽器base64加密的密鑰,server端收到后需
要提取Sec-WebSocket-Key 信息,然后加密。
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat //表?客?端請求提供的可供選擇的?協(xié)議
Sec-WebSocket-Version: 13 //版本標(biāo)識

2.服務(wù)端響應(yīng)、并建立連接

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: SIEylb7zRYJAEgiqJXaOW3V+ZWQ=

3.握手成功促發(fā)客戶端 onOpen 事件

  • 連接狀態(tài)查看

通過ws.readyState 可查看當(dāng)前連接狀態(tài)可選值

  1. CONNECTING (0):表示還沒建立連接。
  2. OPEN (1): 已經(jīng)建立連接,可以進行通訊。
  3. CLOSING (2):通過關(guān)閉握手,正在關(guān)閉連接。
  4. CLOSED (3):連接已經(jīng)關(guān)閉或無法打開。

(二)netty實現(xiàn)websocket演示

源碼:https://github.com/limingIOS/netFuture/tree/master/源碼/『互聯(lián)網(wǎng)架構(gòu)』軟件架構(gòu)-io與nio線程模型reactor模型(上)(53)/nio

源碼:websocket

WebsocketServer.java

package com.dig8.websocket;

import io.netty.bootstrap.ServerBootstrap;

import io.netty.channel.ChannelFuture;

import io.netty.channel.EventLoopGroup;

import io.netty.channel.nio.NioEventLoopGroup;

import io.netty.channel.socket.nio.NioServerSocketChannel;

import io.netty.handler.logging.LogLevel;

import io.netty.handler.logging.LoggingHandler;

public class WebsocketServer {

public static void main(String[] args) throws InterruptedException {

EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup workerGroup = new NioEventLoopGroup();

try{

ServerBootstrap serverBootstrap = new ServerBootstrap();

serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)

.handler(new LoggingHandler(LogLevel.INFO))

.childHandler(new WebSocketChannelInitializer());

ChannelFuture channelFuture = serverBootstrap.bind(8989).sync();

channelFuture.channel().closeFuture().sync();

}finally{

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}

}

WebSocketChannelInitializer.java

package com.dig8.websocket;

import io.netty.channel.ChannelInitializer;

import io.netty.channel.ChannelPipeline;

import io.netty.channel.socket.SocketChannel;

import io.netty.handler.codec.http.HttpObjectAggregator;

import io.netty.handler.codec.http.HttpServerCodec;

import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;

import io.netty.handler.stream.ChunkedWriteHandler;

public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel> {

@Override

protected void initChannel(SocketChannel ch) throws Exception {

ChannelPipeline pipeline = ch.pipeline();

//HttpServerCodec: 針對http協(xié)議進行編解碼

pipeline.addLast("httpServerCodec", new HttpServerCodec());

//ChunkedWriteHandler分塊寫處理,文件過大會將內(nèi)存撐爆

pipeline.addLast("chunkedWriteHandler", new ChunkedWriteHandler());

/**

* 作用是將一個Http的消息組裝成一個完成的HttpRequest或者HttpResponse,那么具體的是什么

* 取決于是請求還是響應(yīng), 該Handler必須放在HttpServerCodec后的后面

*/

pipeline.addLast("httpObjectAggregator", new HttpObjectAggregator(8192));

//用于處理websocket, /ws為訪問websocket時的uri

pipeline.addLast("webSocketServerProtocolHandler", new WebSocketServerProtocolHandler("/ws"));

pipeline.addLast("myWebSocketHandler", new WebSocketHandler());

}

}

WebSocketHandler.java

package com.dig8.websocket;

import io.netty.channel.Channel;

import io.netty.channel.ChannelHandlerContext;

import io.netty.channel.SimpleChannelInboundHandler;

import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.util.Date;

public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

@Override

protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {

Channel channel = ctx.channel();

System.out.println(channel.remoteAddress() + ": " + msg.text());

ctx.channel().writeAndFlush(new TextWebSocketFrame("來自服務(wù)端: " + new Date().toString()));

}

@Override

public void handlerAdded(ChannelHandlerContext ctx) throws Exception {

System.out.println("ChannelId" + ctx.channel().id().asLongText());

}

@Override

public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

System.out.println("用戶下線: " + ctx.channel().id().asLongText());

}

@Override

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

ctx.channel().close();

}

}

test.html

<!DOCTYPE html>
<html>
<head>
 <meta charset="UTF-8">
 <title>Socket</title>
 <script type="text/javascript">
 var websocket;
 //如果瀏覽器支持WebSocket
 if(window.WebSocket){
 websocket = new WebSocket("ws://localhost:8989/ws"); //獲得WebSocket對象
 //當(dāng)有消息過來的時候觸發(fā)
 websocket.onmessage = function(event){
 var respMessage = document.getElementById("respMessage");
 respMessage.value = respMessage.value + "n" + event.data;
 }
 //連接關(guān)閉的時候觸發(fā)
 websocket.onclose = function(event){
 var respMessage = document.getElementById("respMessage");
 respMessage.value = respMessage.value + "n斷開連接";
 }
 //連接打開的時候觸發(fā)
 websocket.onopen = function(event){
 var respMessage = document.getElementById("respMessage");
 respMessage.value = "建立連接";
 }
 }else{
 alert("瀏覽器不支持WebSocket");
 }
 function sendMsg(msg) { //發(fā)送消息
 if(window.WebSocket){
 if(websocket.readyState == WebSocket.OPEN) { //如果WebSocket是打開狀態(tài)
 websocket.send(msg); //send()發(fā)送消息
 }
 }else{
 return;
 }
 }
 </script>
</head>
<body>
<form onsubmit="return false">
 <textarea style="width: 300px; height: 200px;" name="message"></textarea>
 <input type="button" onclick="sendMsg(this.form.message.value)" value="發(fā)送"><br>
 <h3>信息</h3>
 <textarea style="width: 300px; height: 200px;" id="respMessage"></textarea>
 <input type="button" value="清空" onclick="javascript:document.getElementById('respMessage').value = ''">
</form>
</body>
</html>

 

看完讓你徹底搞懂Websocket原理,附通過netty完成Websocket

 

 

看完讓你徹底搞懂Websocket原理,附通過netty完成Websocket

 

 

PS:netty的實現(xiàn)http和websocket基本也就說到這里,具體netty實現(xiàn)RPC這塊我沒演示,我感覺沒必要成熟的框架都是基于netty實現(xiàn)的自己在現(xiàn)實個RPC真沒必要,如果想看netty實現(xiàn)RPC直接看dubbo源碼就可以了。

分享到:
標(biāo)簽:Websocket
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定