1. 程式人生 > >netty同埠監聽tcp和websocket協議

netty同埠監聽tcp和websocket協議

前言

  •   軟體通訊七層結構(osi模型)中由協議套協議最終組成最高階應用層協議(http等等),下三層結構偏向與資料通訊,上三層更偏向於資料處理,中間的傳輸層則是連線上三層與下三層之間的橋樑,每一層都做不同的工作,上層協議依賴與下層協議。
  •   七層結構的最主要功能就是幫助不同系統的主機在不同的網路中進行資料傳輸。
  •   資料傳輸層:tcp、udp協議,tcp協議依賴網際網路協議(ip層協議)。
  •   socket在第五層會話層,它並不是一個協議,而是一組介面(api),更是一個規範,為了方便使用底層協議而存在的一種抽象層。
  •   websocket,http 等協議都是應用層協議(更面向於使用者),依賴於傳輸層tcp協議。
  •   websocket 在進行通訊時,使用了http進行一次握手,資料傳輸使用tcp通道傳輸。
  •   socket更像是一種網路程式設計的概念,是抽象出來的。

tcpSocket 與 websocket 的區別:

  1.  tcpSocket是tcp的協議傳輸,直接進行tcp的三次握手:client向server傳送請求報文,server回覆ack報文並分配資源,client傳送報文並分配資源,連線建立。
  2.  websocket是基於tcp的應用層協議,採用一次HTTP握手。其傳送的請求報文和socket是有區別的。

 

本片文章目的:

  使用netty同埠監聽tcpsocket和websocket訊息傳輸。

 

實現思想:

  在netty程式設計中,對於不同的訊息肯定需要不同的編解碼來處理,所以我們需要利用netty具有動態增刪處理器handle的功能。 

  而從上文中我們知道websocket第一次是採用http握手的,因此debug握手請求,知道他的請求頭部是GET方式。

  所以我們需要根據這點來判斷接收的訊息是websocket還是tcpsocket。

 

 

 

1.判斷handle如下:

/**
 * 協議初始化解碼器.
 *
 * 用來判定實際使用什麼協議.</b>
 *
 */
public class SocketChooseHandle extends ByteToMessageDecoder {
    /** 預設暗號長度為23 */
    private static final int MAX_LENGTH = 23;
    /** WebSocket握手的協議字首 */
    private static final String WEBSOCKET_PREFIX = "GET /";


    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        String protocol = getBufStart(in);
        if (protocol.startsWith(WEBSOCKET_PREFIX)) { SpringApplicationContextHolder.getSpringBeanForClass(PipelineAdd.class).websocketAdd(ctx); ctx.pipeline().remove(LengthFieldBasedFrameDecoder.class); ctx.pipeline().remove(LengthFieldPrepender.class); ctx.pipeline().remove(BytebufToByteHandle.class); } in.resetReaderIndex(); ctx.pipeline().remove(this.getClass()); } private String getBufStart(ByteBuf in){ int length = in.readableBytes(); if (length > MAX_LENGTH) { length = MAX_LENGTH; } // 標記讀位置  in.markReaderIndex(); byte[] content = new byte[length]; in.readBytes(content); return new String(content); } }

說明:判斷是否是GET /協議開始的訊息,如果是,則是websocket,那麼移除後面的socket編解碼器,新增websocket編解碼器。然後刪除自己(將自身刪除後,此連線之後的訊息將不再進入這個handle,直接走安排好的對應handle序列處理器。而不是所有連線)。

 

 

2.接下來我們將上述handle判斷器加入到訊息來的編解碼器前方。

實現程式碼如下:

     // 1.socket方式服務
            // 設定N秒沒有讀到資料,則觸發一個READER_IDLE事件。
            pipeline.addLast(new IdleStateHandler(readerIdleTime,0,0, TimeUnit.SECONDS));
            pipeline.addLast("active",new ChannelActiveHandle());
            pipeline.addLast("socketChoose",new SocketChooseHandle());

            //tcpsocket編碼解碼handle,如果是websocket連結,會將其刪除
            pipeline.addLast("lengthEncode",new LengthFieldPrepender(4, false));
            pipeline.addLast("lengthDecoder",new LengthFieldBasedFrameDecoder(2000, 0, 4,0, 4));
            pipeline.addLast(bytebufToByteHandle);

            //因為接收型別的泛型不對,所以在websocket握手的時候不會進入該handle
            //此handle為最後的socket訊息分解,web和tcp通用
            pipeline.addLast("byteToBuf",byteToByteBufHandle);
            pipeline.addLast("protocolResolve",protocolResolveHandle);

說明:

IdleStateHandler  心跳處理器
active 活躍連線處理器
socketChoose 前文所述的判斷訊息協議處理器
lengthEncode tcp連線的拆包粘包編碼處理器(用來在訊息前面附加4個位元組的長度資訊)
lengthDecoder tcp連線的拆包粘包解碼處理器(長度判斷)
bytebufToByteHandle  自定義處理器,用來將bytebuf轉成我們需要的byte[]。
byteToBuf  自定義處理器,用來將要發出的訊息從byte[]裝成bytebuf。
protocolResolve 自定義處理器,處理訊息,找到路由handle以及處理邏輯



3.websocket需要的編解碼器
程式碼如下
  public  void websocketAdd(ChannelHandlerContext ctx){

        // HttpServerCodec:將請求和應答訊息解碼為HTTP訊息
        ctx.pipeline().addBefore("byteToBuf","http-codec",new HttpServerCodec());

        // HttpObjectAggregator:將HTTP訊息的多個部分合成一條完整的HTTP訊息
        ctx.pipeline().addBefore("byteToBuf","aggregator",new HttpObjectAggregator(65535));

        // ChunkedWriteHandler:向客戶端傳送HTML5檔案
        ctx.pipeline().addBefore("byteToBuf","http-chunked",new ChunkedWriteHandler());

        ctx.pipeline().addBefore("byteToBuf","WebSocketAggregator",new WebSocketFrameAggregator(65535));

        // 在管道中新增我們自己的接收資料實現方法
        ctx.pipeline().addBefore("byteToBuf","ws-handShake",wsHandShakeServerHandle);

        // 後續直接走訊息處理
        ctx.pipeline().addBefore("byteToBuf","wsPack",wsPacketHandle);

        // 編碼。將通用byteBuf編碼成binaryWebSocketFrame.通過前面的編碼器
        ctx.pipeline().addBefore("byteToBuf","bufToFrame",bytebufToBinaryFrameHandle);


    }

說明:

wsPacketHandle   自定義處理器,將接收的BinaryWebSocketFrame訊息轉成byte[]供protocolResolve處理。
bytebufToBinaryFrameHandle  自定義處理器,將需要傳送的byte轉成BinaryWebSocketFrame供websocket的編碼器處理。


 

詳細程式碼請參考nafos-network: https://gitee.com/huangxinyu/BC-NETTYGO

 


 

NAFOS

一個基於netty的輕量級高效能服務端框架。 

簡介

nafos是一個基於netty的高效能伺服器框架,其目的在於易上手,易擴充套件,讓開發人員更致力於業務開發。 在前後端分離的web架構上,或者APP,手遊,nafos都是一個很不錯的選擇。

除此之外,sample中也給出了超簡單的擴充套件方案,使得nafos在分散式擴充套件上能更勝一籌。

文件

特點

  • 1、簡單易用:通過簡單的配置檔案即可建立完善的啟動方案,然後就可以開心的關注業務程式碼了;
  • 2、序列設計 :單使用者的所有請求都是序列進行,完美解決單使用者併發問題,減少鎖的使用;
  • 3、高效能:網路層採用netty作為中介軟體,同等配置及優化條件下,相比tomcat效能可提升一倍;
  • 4、易擴充套件:整合了springBoot,可完美支援spring大家族系列;
  • 5、強相容: 可單機同時支援HTTP,TCP,websocket等服務,小規模應用下不用多開佔用資源;
  • 6、工具類豐富:封裝所有開發中常見工具類可直接呼叫;
  • 7、房間策略:封裝常見遊戲的房間策略,開房,比賽,聊天可直接呼叫,無需多寫;
  • 8、模組化:多個模組之間相互解耦,喜歡哪個用哪個,不喜歡直接丟棄。
  • 9、指令碼支援:內有現成的shell指令碼可以直接使用,開關機,資料庫備份等;
  • 10、自帶分散式限流器,有IP策略和總流量策略等漏桶限流,抵禦攻擊。
  • 11、豐富教程:除了詳細文件外,在sample模組中還有多模組使用案例,開發無憂~