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模塊中還有多模塊使用案例,開發無憂~

 


免責聲明!

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



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