日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

netty系列之:使用netty搭建websocket服务器

發(fā)布時(shí)間:2024/2/28 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 netty系列之:使用netty搭建websocket服务器 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

文章目錄

  • 簡介
  • netty中的websocket
    • websocket的版本
    • FrameDecoder和FrameEncoder
    • WebSocketServerHandshaker
    • WebSocketFrame
  • netty中使用websocket
  • 總結(jié)

簡介

websocket是一個(gè)優(yōu)秀的協(xié)議,它是建立在TCP基礎(chǔ)之上的,兼容HTTP的網(wǎng)絡(luò)協(xié)議。通過Websocket我們可以實(shí)現(xiàn)客戶端和服務(wù)器端的即時(shí)通訊,免除了客戶端多次輪循帶來的性能損耗。

既然websocket這么優(yōu)秀,那么怎么在netty中使用websocket呢?

netty中的websocket

雖然websocket是一個(gè)單獨(dú)的和HTTP協(xié)議完全不同的協(xié)議,但是在netty中還是將其放到了http包中。我們回想一下netty中對于各種協(xié)議的支持。如果要支持這種協(xié)議,肯定需要一個(gè)decoder和encoder編碼和解碼器用于對協(xié)議進(jìn)行編解碼。將傳輸?shù)臄?shù)據(jù)從ByteBuf轉(zhuǎn)換到協(xié)議類型,或者將協(xié)議類型轉(zhuǎn)換成為ByteBuf。

這是netty的工作核心原理,也是后續(xù)自定義netty擴(kuò)展的基礎(chǔ)。

那么對于websocket來說,是怎么樣的呢?

websocket的版本

WebSocket作為一種協(xié)議,自然不是憑空而來的,通過不斷的發(fā)展才到了今天的WebSocket協(xié)議。具體的webSocket的發(fā)展史我們就不去深究了。我們先看下netty提供的各種WebSocket的版本。

在WebSocketVersion類中,我們可以看到:

UNKNOWN(AsciiString.cached(StringUtil.EMPTY_STRING)),V00(AsciiString.cached("0")),V07(AsciiString.cached("7")),V08(AsciiString.cached("8")),V13(AsciiString.cached("13"));

WebSocketVersion是一個(gè)枚舉類型,它里面定義了websocket的4個(gè)版本,除了UNKNOWN之外,我們可以看到websocket的版本有0,7,8,13這幾個(gè)。

FrameDecoder和FrameEncoder

我們知道websocket的消息是通過frame來傳遞的,因?yàn)椴煌瑆ebsocket的版本影響到的是frame的格式的不同。所以我們需要不同的FrameDecoder和FrameEncoder來在WebSocketFrame和ByteBuf之間進(jìn)行轉(zhuǎn)換。

既然websocket有四個(gè)版本,那么相對應(yīng)的就有4個(gè)版本的decoder和encoder:

WebSocket00FrameDecoder WebSocket00FrameEncoder WebSocket07FrameDecoder WebSocket07FrameEncoder WebSocket08FrameDecoder WebSocket08FrameEncoder WebSocket13FrameDecoder WebSocket13FrameEncoder

至于每個(gè)版本之間的frame有什么區(qū)別,我們這里就不細(xì)講了,感興趣的朋友可以關(guān)注我的后續(xù)文章。

熟悉netty的朋友應(yīng)該都知道,不管是encoder還是decoder都是作用在channel中對消息進(jìn)行轉(zhuǎn)換的。那么在netty中對websocket的支持是怎么樣的呢?

WebSocketServerHandshaker

netty提供了一個(gè)WebSocketServerHandshaker類來統(tǒng)一使用encoder和decoder的使用。netty提供一個(gè)工廠類WebSocketServerHandshakerFactory根據(jù)客戶端請求header的websocket版本不同,來返回不同的WebSocketServerHandshaker。

public WebSocketServerHandshaker newHandshaker(HttpRequest req) {CharSequence version = req.headers().get(HttpHeaderNames.SEC_WEBSOCKET_VERSION);if (version != null) {if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).return new WebSocketServerHandshaker13(webSocketURL, subprotocols, decoderConfig);} else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {// Version 8 of the wire protocol - version 10 of the draft hybi specification.return new WebSocketServerHandshaker08(webSocketURL, subprotocols, decoderConfig);} else if (version.equals(WebSocketVersion.V07.toHttpHeaderValue())) {// Version 8 of the wire protocol - version 07 of the draft hybi specification.return new WebSocketServerHandshaker07(webSocketURL, subprotocols, decoderConfig);} else {return null;}} else {// Assume version 00 where version header was not specifiedreturn new WebSocketServerHandshaker00(webSocketURL, subprotocols, decoderConfig);}}

同樣的, 我們可以看到,netty為websocket也定義了4種不同的WebSocketServerHandshaker。

WebSocketServerHandshaker中定義了handleshake方法,通過傳入channel,并向其添加encoder和decoder

public final ChannelFuture handshake(Channel channel, FullHttpRequest req,HttpHeaders responseHeaders, final ChannelPromise promise) p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder());p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder());

而添加的這兩個(gè)newWebSocketEncoder和newWebsocketDecoder就是各個(gè)WebSocketServerHandshaker的具體實(shí)現(xiàn)中定義的。

WebSocketFrame

所有的ecode和decode都是在WebSocketFrame和ByteBuf中進(jìn)行轉(zhuǎn)換。WebSocketFrame繼承自DefaultByteBufHolder,表示它是一個(gè)ByteBuf的容器。除了保存有ByteBuf之外,它還有兩個(gè)額外的屬性,分別是finalFragment和rsv。

finalFragment表示該frame是不是最后一個(gè)Frame。對于大數(shù)據(jù)量的消息來說,會(huì)將消息拆分成為不同的frame,這個(gè)屬性特別有用。

我們再看一下websocket協(xié)議消息的格式:

0 1 2 30 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-------+-+-------------+-------------------------------+|F|R|R|R| opcode|M| Payload len | Extended payload length ||I|S|S|S| (4) |A| (7) | (16/64) ||N|V|V|V| |S| | (if payload len==126/127) || |1|2|3| |K| | |+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +| Extended payload length continued, if payload len == 127 |+ - - - - - - - - - - - - - - - +-------------------------------+| |Masking-key, if MASK set to 1 |+-------------------------------+-------------------------------+| Masking-key (continued) | Payload Data |+-------------------------------- - - - - - - - - - - - - - - - +: Payload Data continued ... :+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +| Payload Data continued ... |+---------------------------------------------------------------+

rsv代表的是消息中的擴(kuò)展字段,也就是RSV1,RSV2和RSV3。

除此之外就是ByteBuf的一些基本操作了。

WebSocketFrame是一個(gè)抽象類,它的具體實(shí)現(xiàn)類有下面幾種:

BinaryWebSocketFrame CloseWebSocketFrame ContinuationWebSocketFrame PingWebSocketFrame PongWebSocketFrame TextWebSocketFrame

BinaryWebSocketFrame和TextWebSocketFrame很好理解,他們代表消息傳輸?shù)膬煞N方式。

CloseWebSocketFrame是代表關(guān)閉連接的frame。ContinuationWebSocketFrame表示消息中多于一個(gè)frame的表示。

而PingWebSocketFrame和PongWebSocketFrame是兩個(gè)特殊的frame,他們主要用來做服務(wù)器和客戶端的探測。

這些frame都是跟Websocket的消息類型一一對應(yīng)的,理解了websocket的消息類型,對應(yīng)理解這些frame類還是很有幫助的。

netty中使用websocket

講了這么多websocket的原理和實(shí)現(xiàn)類,接下來就是實(shí)戰(zhàn)了。

在這個(gè)例子中,我們使用netty創(chuàng)建一個(gè)websocket server,然后使用瀏覽器客戶端來對server進(jìn)行訪問。

創(chuàng)建websocket server和普通netty服務(wù)器的過程沒有什么兩樣。只是在ChannelPipeline中,需要加入自定義的WebSocketServerHandler:

pipeline.addLast(new WebSocketServerHandler());

這個(gè)WebSocketServerHandler需要做什么事情呢?

它需要同時(shí)處理普通的HTTP請求和webSocket請求。

這兩種請求可以通過接收到的msg類型的不同來進(jìn)行判斷:

public void channelRead0(ChannelHandlerContext ctx, Object msg) throws IOException {//根據(jù)消息類型,處理兩種不同的消息if (msg instanceof FullHttpRequest) {handleHttpRequest(ctx, (FullHttpRequest) msg);} else if (msg instanceof WebSocketFrame) {handleWebSocketFrame(ctx, (WebSocketFrame) msg);}}

在客戶端進(jìn)行websocket連接之前,需要借用當(dāng)前的channel通道,開啟handleshake:

// websocket握手WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null, true, 5 * 1024 * 1024);handshaker = wsFactory.newHandshaker(req);if (handshaker == null) {WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());} else {handshaker.handshake(ctx.channel(), req);}

我們得到handshaker之后,就可以對后續(xù)的WebSocketFrame進(jìn)行處理:

private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {// 處理各種websocket的frame信息if (frame instanceof CloseWebSocketFrame) {handshaker.close(ctx, (CloseWebSocketFrame) frame.retain());return;}if (frame instanceof PingWebSocketFrame) {ctx.write(new PongWebSocketFrame(frame.content().retain()));return;}if (frame instanceof TextWebSocketFrame) {// 直接返回ctx.write(frame.retain());return;}if (frame instanceof BinaryWebSocketFrame) {// 直接返回ctx.write(frame.retain());}}

這里我們只是機(jī)械的返回消息,大家可以根據(jù)自己業(yè)務(wù)邏輯的不同,對消息進(jìn)行解析。

有了服務(wù)器端,客戶端該怎么連接呢?很簡單首選構(gòu)造WebSocket對象,然后處理各種回調(diào)即可:

socket = new WebSocket("ws://127.0.0.1:8000/websocket"); socket.onmessage = function (event) { } socket.onopen = function(event) {}; socket.onclose = function(event) {};

總結(jié)

以上就是使用netty搭建websocket服務(wù)器的完整流程,本文中的服務(wù)器可以同時(shí)處理普通HTTP請求和webSocket請求,但是稍顯復(fù)雜,有沒有更加簡單的方式呢?敬請期待。

本文的例子可以參考:learn-netty4

本文已收錄于 http://www.flydean.com/23-netty-websocket-server/

最通俗的解讀,最深刻的干貨,最簡潔的教程,眾多你不知道的小技巧等你來發(fā)現(xiàn)!

歡迎關(guān)注我的公眾號:「程序那些事」,懂技術(shù),更懂你!

超強(qiáng)干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生

總結(jié)

以上是生活随笔為你收集整理的netty系列之:使用netty搭建websocket服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。