Netty之WebSocket協議開發
開始
在本章的開頭把程式碼奉上,大家下載下來對照的學習,這些程式碼都是執行通過的。
上節我們講解了HTTP協議開發,但是,Http協議的開銷問題,導致它們不適用於低延遲的應用。為了解決這些問題,我們引入了webSocket。
HTTP協議的弊端
我們來總結一下HTTP協議的弊端:
1.HTTP協議是半雙工的協議。大家知道對講機嗎?它就是半雙工的裝置。當對方在說話時,你就不能說話了,也就是說一個時間點上,只能一個人在說話。現在已經被全雙工的電話給替代掉了,現在對講機這東西用的不多了。 2.HTTP協議冗長而繁瑣。HTTP訊息包含訊息頭,訊息體,換行符等,採用文字形式傳播,相比二進位制通訊協議,非常冗長且繁瑣。 3.針對伺服器推送的黑客攻擊。例如長時間輪詢。
為了解決以上HTTP協議效率底下的問題,WebSocket協議應運而生,它能更好的節約頻寬和伺服器資源並且實時通訊。下面就讓我們看看websocket協議的神奇之處。
WebSocket協議的特點
1.單一的TCP連線,採用全雙工通訊
2.對代理,防火牆,路由器透明。
3.無頭部資訊,cookie和身份驗證
4.無安全開銷
5.通過ping幀保持鏈路啟用
6.伺服器可以主動的對客戶端進行傳送訊息,而不需要輪詢。
總的來說我們webSocket被設計出來的目的就是為了取代輪詢這種高消耗的方式。
websocket連線過程
建立連線過程如下:
1.客戶端瀏覽器首先要向伺服器傳送一個HTTP請求,這個請求和通常的HTTP請求不同,包含了一些附加資訊,其中附加資訊“UpgradeWebSocket”表明這是一個申請協議升級的http請求。 2.伺服器解析這些附加頭資訊,然後生成應答資訊返回給客戶端,這樣客戶端和伺服器端的webSocket連線就建立起來了 3.這個連線會一直持續到客戶端或者伺服器的某一方主動關閉連線。
我們來看一下客戶端向伺服器請求的訊息:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade:websocket
Connection:Upgrade
Sec-WeSocket-Key:dGhlweidxlkjsdZQ==
Origin:http://example.com
Sec-WebSocket-Protocol:chat,superchat
Sec-webSocket-Version:13
我們來看一下伺服器返回給客戶端的應答訊息:
HTTP/1.1 101 Switching Protocols Upgrade:websocket Connection:Upgrade Sec-WebSocket-accept:siefjosigdfsl= Sec-WebSocket-Protocol:chat
請求訊息中的“Sec-WebSocket-Key”是隨機的,伺服器端會用這些資料來構造出一個SHA-1的資訊摘要,把“Sec-WebSocket-Key”加上一個魔幻字串“25EAFA5-E914-47DA-95CA-C54B0DC85B11”.使用SHA-1加密,然後進行BASE-64編碼,將結果作為Sec-WebSocket-accept頭的值,返回給客戶端。
連線示意圖如下:
websocket生命週期
握手成功後,客戶端和伺服器就可以通過”message”方式進行通訊了,一個訊息由一個或者多個幀組成。
websocket連線關閉。
為關閉websocket連線,客戶端和伺服器需要通過一個安全的方法關閉底層TCP連線及TLS會話。如果合適,丟棄任何可能接收的位元組:必要時可以通過任何可用的手段關閉連線。
編碼開始
需求:我們要編寫一個webSocket伺服器,支援WebSocket的瀏覽器通過webSocket協議傳送請求給我們編寫的webSocket伺服器,伺服器對請求訊息進行判斷,如果是合法的webSocket請求,則獲取請求訊息體,並在後面追加字串:“歡迎使用Netty WebSocket 服務,現在時刻:系統時間”。
當然客戶端也是我們編寫的,其中內嵌js指令碼去建立webSocket連線,如果握手成功列印:“開啟websocket 服務正常,瀏覽器支援Websocket!”
首先對webSocket伺服器的功能進行簡單講解。websocket服務端接收到請求訊息後,先對訊息的型別進行判斷,如圖所示:
我們就是判斷upgrade取它的值判斷是否是websocket,如果不是webSocket型別的請求訊息,則返回HTTP 400 BAD REQUEST 響應給客戶端。如果是伺服器返回訊息,雙方連線正式建立。
由於篇幅問題,只對核心程式碼講解,全部程式碼大家可以下載下來看看。
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//HttpServerCodec的作用是將請求和應答訊息編碼或者解碼為HTTP訊息;
ch.pipeline().addLast("http-codec", new HttpServerCodec());
//使用HttpObjectAggregator會把多個訊息轉換為一個單一的FullHttpRequest或是FullHttpResponse
ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
//支援處理非同步傳送大資料檔案,但不佔用過多的記憶體,防止發生記憶體洩漏,這裡是向客戶端傳送html5檔案
ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
//這個是我們自定義的,處理檔案伺服器邏輯。主要功能還是在這個檔案中
ch.pipeline().addLast("http-fileServerHandler", new WebSocketServerHandler());
}
});
Channel ch = b.bind(port).sync().channel();//這裡寫你本機的IP地址
System.out.println("web socket server started at port "+port+".");
System.out.println("open your browser and navigate to http://localhost:"+port+"/");
ch.closeFuture().sync();
} catch (Exception e) {
}finally{
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
講解:
HttpServerCodec講解:HTTP協議的請求解碼器和響應編碼器即HttpServerCodec,它會將HTTP客戶端請求轉成HttpRequest物件,將HttpResponse物件編碼成HTTP響應傳送給客戶端。
HttpObjectAggregator講解:使用HttpObjectAggregator會把多個訊息轉換為一個單一的FullHttpRequest或是FullHttpResponse
下面我們來看一下核心類WebSocketServerHandler,我都做的詳細的註釋,希望大家能看懂。
/**
* @author 作者 YYD
* @version 建立時間:2016年8月17日 上午6:32:23
* @function 未新增
*/
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object>{
private static final Logger logger = Logger.getLogger(WebSocketServerHandler.class.getName());
private WebSocketServerHandshaker handshaker;
/**
* 接收客戶端發過來的訊息並處理
* FullHttpRequest :
* 官網解釋:Combine the {@link HttpRequest} and {@link FullHttpMessage}, so the request is a <i>complete</i> HTTP
* request.
* 這個請求是 代表http請求完成的標記。
*/
@Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg)
throws Exception {
if(msg instanceof FullHttpRequest){//接收到客戶端的握手請求,開始處理握手
handleHttpRequest(ctx,(FullHttpRequest)msg);
}else if(msg instanceof WebSocketFrame){//接收到客戶端發過來的訊息(只過濾文字訊息),處理後發給客戶端。
handleWebSocketFrame(ctx, (WebSocketFrame)msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
private void handleHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req) throws Exception{
/**
* 如果不成功或者訊息頭不包含"Upgrade",說明不是websocket連線,報400異常。
*/
if(!req.getDecoderResult().isSuccess()||(!"websocket".equals(req.headers().get("Upgrade")))){
sendHttpResponse(ctx,req,new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.BAD_REQUEST));
return;
}
/**
* WebSocket是一種全新的協議,不屬於http無狀態協議,協議名為"ws",這意味著一個websocket連線地址會是這樣的寫法:
ws://127.0.0.1:8080/websocket。ws不是http,所以傳統的web伺服器不一定支援,需要伺服器與瀏覽器同時支援, WebSocket才能正常執行,目前大部分瀏覽器都支援Websocket。
WebSocketServerHandshaker 官網的解釋是:伺服器端Web套接字開啟和關閉握手基類
WebSocketServerHandshakerFactory 官網的解釋是:自動檢測正在使用的網路套接字協議的版本,並建立一個新的合適的 WebSocketServerHandshaker。
*/
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:8080/websocket",null,false);
handshaker = wsFactory.newHandshaker(req);//建立一個握手協議
if(handshaker == null){
/**
* Return that we need cannot not support the web socket version
* 返回不支援websocket 版本
*/
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
}else {
handshaker.handshake(ctx.channel(), req);//開始握手
}
}
/**
* 我們判斷資料型別,只支援文字型別
* @param ctx
* @param frame
*/
private void handleWebSocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame) {
if(frame instanceof CloseWebSocketFrame){//如是接收到的是關閉websocket,就關閉連線
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
return;
}
if(frame instanceof PingWebSocketFrame){//如果資訊是2進位制資料,就反給它,
ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
return;
}
if(!(frame instanceof TextWebSocketFrame)){//哪果不是文字的資料,就報錯。
throw new UnsupportedOperationException(String.format("%s frame types not supported",frame.getClass().getName()));
}
String request = ((TextWebSocketFrame)frame).text();
if(logger.isLoggable(Level.FINE)){
logger.fine(String.format("%s receive %s",ctx.channel(),request));
}
ctx.channel().write(new TextWebSocketFrame(request+",歡迎使用netty websocket 服務,現在時刻"+new Date().toString()));
}
private static void sendHttpResponse(ChannelHandlerContext ctx,FullHttpRequest req,FullHttpResponse res){
if(res.getStatus().code() != 200){
ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(),CharsetUtil.UTF_8);
res.content().writeBytes(buf);
buf.release();
setContentLength(res,res.content().readableBytes());
}
ChannelFuture f = ctx.channel().writeAndFlush(res);//傳送訊息
if(!isKeepAlive(req)||res.getStatus().code()!= 200){//如果斷開連線,或者傳送不成功,斷開連線。
f.addListener(ChannelFutureListener.CLOSE);//關閉連線
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
cause.printStackTrace();
ctx.close();
}
}
上面的程式碼,我都做的詳細的註釋,大家可以對照著看看。
在handleHttpRequest方法中我們判斷訊息頭中是否包含“Upgrade”欄位,如果不包含就返回Http 400 響應。握手請求經過簡單校驗後,我們通過構造握手工廠,建立握手出理類webSocketServerHandshaker,通過它構造握手響應訊息返回給客戶端,同時將webSocket訊息的編碼和解碼類動態新增到ChannelPipeline中,用於websocket訊息的編解碼。
下面來看看客戶端瀏覽器程式碼
<html>
<head>
<meta charset="UTF-8">
netty websocket 時間伺服器
</head>
<br>
<body>
<br>
<script type="text/javascript">
var socket;
if(!window.WebSocket){
window.WebSocket = window.MozWebSocket;
}
if(window.WebSocket){
var socket = new WebSocket("ws://localhost:9090/websocket");
socket.onmessage = function(event){
var ta = document.getElementById('responseText');
ta.value="";
ta.value = event.data
};
socket.onopen = function(event){
var ta = document.getElementById('responseText');
ta.value = '';
ta.value = "開啟websocket服務正常,瀏覽器支援websocket!";
};
socket.onclose = function(event){
var ta = document.getElementById('responseText');
ta.value = '';
ta.value = "websocket關閉!";
};
}else{
alert("抱歉,您的瀏覽器不支援WebSocket 協議!");
}
function send(message){
if(!window.WebSocket){
return;
}
if(socket.readyState == window.WebSocket.OPEN){
socket.send(message);
}else{
alert("WebSocket 還沒有建立連線!")
}
}
</script>
<form onsubmit="return false;">
<input type="text" name="message" vaule="netty最佳實踐"/>
<br><br>
<input type="button" value="傳送 WebSocket 請求訊息" onclick="send(this.form.message.value)">
<hr color="blue"/>
<h3>服務端返回的應答訊息</h3>
<textarea id="responseText" style"width:500px;height:300px;"></textarea>
</form>
</body>
</html>
我們這裡不加講解了,大家看看就明白了。
看看客戶端的執行效果
初始狀態效果圖
傳送訊息效果圖
結尾
在本章的結尾把程式碼奉上,大家下載下來對照的學習,這些程式碼都是執行通過的。
好了就講到這裡吧,由於HttP協議本身存在的弊端,產生了websocket協議。希望對大家有所幫助。有不懂的可以聯絡我[email protected]
同時鼓勵自己,堅持就會有奇蹟,加油!
相關推薦
Netty之WebSocket協議開發
開始 在本章的開頭把程式碼奉上,大家下載下來對照的學習,這些程式碼都是執行通過的。 上節我們講解了HTTP協議開發,但是,Http協議的開銷問題,導致它們不適用於低延遲的應用。為了解決這些問題,我們引入了webSocket。 HTTP協議的弊端 我們來總
Netty筆記:使用WebSocket協議開發聊天系統
前言,之前一直圍繞著Http協議來開發專案,最近由於參與一個類似競拍專案的開發,有這樣一個場景,多個客戶端競拍一個商品,當一個客戶端加價後,其它關注這個商品的客戶端需要立即知道該商品的最新價格。這裡有個問題,Http協議是基於請求/響應的,客戶端傳送請求,然後
SpringBoot整合Netty之Websocket
前後端通過websocket通訊進行聊天~ 核心程式碼整理如下: netty元件 @Component public class NettyBooter implements ApplicationListener<ContextRefreshedEvent> {
python之WebSocket的開發
## websocket.py import socket import struct import hashlib,base64 import threading,random #執行緒,套接字,雜湊表,隨機數 #存放連結客戶fd,元組 connectionlist =
Netty自定義協議開發
自定義協議格式 %1$8s%2$4s%3$4s%4$8s%5$16s%6$32s%7$8s 類似:AAAAAAAA0001000011001001usernameusernamesession1session1session1session100000023
Swift-核心之面向協議開發
Swift is a Protocol-Oriented Programming Language Swift 是一門面向協議 (POP) 開發的語言 Swift 的核心是面向協議程式設計 WWDC 對 OOP 很好的詮釋: POP 面向協議
python之WebSocket協議
一、WebSocket理論部分 1、websocket是什麼 Websocket是html5提出的一個協議規範,參考rfc6455。 websocket約定了一個通訊的規範,通過一個握手的機制,客戶端(瀏覽器)和伺服器(webserver)之間能建立一個類似tcp的連線
不惑之年的硬體牛人轉到軟體自學之netty框架(六)Netty的網路協議:WebSocket和UDP
由於近期開發一箇中型的物聯專案,帶著十來個兄弟從底層硬體到無線局域通訊到通用閘道器到netty高可用框架到spring cloud的後臺開發到移動端APP開發到WEB前端的開發整體框架的搭建,雖然很辛苦,但我一直在給兄弟們說我們要三年內在物聯行業佔有一席之地,期待專案的成功。
netty同埠監聽tcp和websocket協議
前言: 軟體通訊七層結構(osi模型)中由協議套協議最終組成最高階應用層協議(http等等),下三層結構偏向與資料通訊,上三層更偏向於資料處理,中間的傳輸層則是連線上三層與下三層之間的橋樑,每一層都做不同的工作,上層協議依賴與下層協議。 七層結構的最主要功能就是幫助不同系統的主機在不同的網
Netty入門之WebSocket初體驗
說一說IO通訊 BIO通訊: BIO即同步阻塞模式一請求一應答的通訊模型,該模型最大的問題就是缺乏彈性伸縮能力,當客戶端併發訪問量增加後,服務端的執行緒個數和客戶端併發訪問數呈1:1的正比關係,由於執行緒是JAVA虛擬機器非常寶貴的系統資源,當執行緒數膨脹之後,系統的效能將急劇下降,隨著併發訪問量的繼續增
SpringBoot | 第十九章:web 應用開發之 WebSocket
前言 web開發也講解了三章了,這章節開始講解關於與前端通訊相關知識。實現一個線上聊天室類似的功能或者後端推送訊息到前端,在沒有WebSocket時,讀大學那夥還有接觸過DWR(Direct Web Remoting),也使用過輪詢的方式,當Servlet3.0出來後,也有
dotnet core 開發無縫相容Http和Websocket協議的介面服務
在應用介面開發中往往要針對不同協義開發相應的代理服務,但對於Websocket和http這兩種協議來說就有些不同,從實現上來看Websocket可以說是Http的升級子協議, 兩者在協議處理上基本一致,具體可以在解釋Body上有所不同。FastHttpApi在實現過程完全支援http協議升級成websocke
Netty協議開發(HTTP)
HTTP是一個屬於應用層的面向物件協議,由於其簡捷、快速的方式,適用於分散式超媒體資訊系統。 HTTP協議的URL http://host[":"port][abs_path] http表示通過HTTP協議來定位網路資源;host表示合法的Internet主機域名或者I
Springboot 整合Websocket 註解開發之第一步瀏覽器和伺服器建立連線(解決了建立連線時404錯誤!!!!)
1、建立一個springboot專案 勾選web和websocket選項 建立完成後build.gradle檔案如下,主要是依賴得新增上(另外說明以下compile('org.springframework.boot:spring-boot-starter-w
【轉】Netty之解決TCP粘包拆包(自定義協議)
https://www.cnblogs.com/sidesky/p/6913109.html 1、什麼是粘包/拆包 一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方
snmp 協議開發之HiliSoft MIB Browser(輔助開發工具)
為了簡單起見,現在在Windows 7上建立一個SNMP服務環境,windows 7系統也自帶了該服務的支援! 控制面板中開啟新增或刪除程式! 安裝後會在服中看到SNMP服務內容: 然後我們需要安裝一個軟體,這個軟體可以檢視機器的MIB樹: HiliSo
Netty入門之WebSocket(二)
什麼是WebSocket:是一種H5協議規範 解決客戶端與服務端實時通訊而產生的技術:WebSocket本質是一種基於TCP協議,先通過Http/Https發一個特殊的Http請求進行握手,握手後會建立一個用於交換資料的TCP連結,之後客戶端和服務端使用該TCP連結進行實時
Jmeter之RPC協議指令碼開發實現
1、首先通過本文是通過jmeter的java請求方式實現RPC協議指令碼請求,具體關於java請求如果基於jmeter實現見:jmeter之java請求;如果還想了解rpc指令碼開發詳細見:rpc協議之hprose介面測試 。 2、分別需要建立兩個類 send類、Ap
Netty之解決TCP粘包拆包(自定義協議)
1、什麼是粘包/拆包 一般所謂的TCP粘包是在一次接收資料不能完全地體現一個完整的訊息資料。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理資料,再加上網路上MTU的往往小於在應用處理的訊息資料,所以就會引發一次接收的資料無法滿足訊息的需要,導
Android開發技術網路篇之── http協議post請求方式
Java程式碼 程式碼如下: private Button button1,button2,button3; private TextView textView1; button1.setOnClickListener(new Button.OnClickListener(){