<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                #### 5.2 消息的封包與拆包 我們這里就是采用經典的TLV\(Type-Len-Value\)封包格式來解決TCP粘包問題吧。 ![](https://img.kancloud.cn/06/33/06336f097f9db897457e35f1b0399533_1024x768.jpeg)由于Zinx也是TCP流的形式傳播數據,難免會出現消息1和消息2一同發送,那么zinx就需要有能力區分兩個消息的邊界,所以Zinx此時應該提供一個統一的拆包和封包的方法。在發包之前打包成如上圖這種格式的有head和body的兩部分的包,在收到數據的時候分兩次進行讀取,先讀取固定長度的head部分,得到后續Data的長度,再根據DataLen讀取之后的body。這樣就能夠解決粘包的問題了。 ##### A\) 創建拆包封包抽象類 在`zinx/ziface`下,創建`idatapack.go`文件 > zinx/ziface/idatapack.go ```go package ziface /* 封包數據和拆包數據 直接面向TCP連接中的數據流,為傳輸數據添加頭部信息,用于處理TCP粘包問題。 */ type IDataPack interface{ GetHeadLen() uint32 //獲取包頭長度方法 Pack(msg IMessage)([]byte, error) //封包方法 Unpack([]byte)(IMessage, error) //拆包方法 } ``` ##### B\) 實現拆包封包類 在`zinx/znet/`下,創建`datapack.go`文件. > zinx/znet/datapack.go ```go package znet import ( "bytes" "encoding/binary" "errors" "zinx/utils" "zinx/ziface" ) //封包拆包類實例,暫時不需要成員 type DataPack struct {} //封包拆包實例初始化方法 func NewDataPack() *DataPack { return &DataPack{} } //獲取包頭長度方法 func(dp *DataPack) GetHeadLen() uint32 { //Id uint32(4字節) + DataLen uint32(4字節) return 8 } //封包方法(壓縮數據) func(dp *DataPack) Pack(msg ziface.IMessage)([]byte, error) { //創建一個存放bytes字節的緩沖 dataBuff := bytes.NewBuffer([]byte{}) //寫dataLen if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetDataLen()); err != nil { return nil, err } //寫msgID if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetMsgId()); err != nil { return nil, err } //寫data數據 if err := binary.Write(dataBuff, binary.LittleEndian, msg.GetData()); err != nil { return nil ,err } return dataBuff.Bytes(), nil } //拆包方法(解壓數據) func(dp *DataPack) Unpack(binaryData []byte)(ziface.IMessage, error) { //創建一個從輸入二進制數據的ioReader dataBuff := bytes.NewReader(binaryData) //只解壓head的信息,得到dataLen和msgID msg := &Message{} //讀dataLen if err := binary.Read(dataBuff, binary.LittleEndian, &msg.DataLen); err != nil { return nil, err } //讀msgID if err := binary.Read(dataBuff, binary.LittleEndian, &msg.Id); err != nil { return nil, err } //判斷dataLen的長度是否超出我們允許的最大包長度 if (utils.GlobalObject.MaxPacketSize > 0 && msg.DataLen > utils.GlobalObject.MaxPacketSize) { return nil, errors.New("Too large msg data recieved") } //這里只需要把head的數據拆包出來就可以了,然后再通過head的長度,再從conn讀取一次數據 return msg, nil } ``` 需要注意的是整理的`Unpack`方法,因為我們從上圖可以知道,我們進行拆包的時候是分兩次過程的,第二次是依賴第一次的dataLen結果,所以`Unpack`只能解壓出包頭head的內容,得到msgId 和 dataLen。之后調用者再根據dataLen繼續從io流中讀取body中的數據。 ##### C\) 測試拆包封包功能 為了容易理解,我們先不用集成zinx框架來測試,而是單獨寫一個Server和Client來測試一下封包拆包的功能 > Server.go ```go package main import ( "fmt" "io" "net" "zinx/znet" ) //只是負責測試datapack拆包,封包功能 func main() { //創建socket TCP Server listener, err := net.Listen("tcp", "127.0.0.1:7777") if err != nil { fmt.Println("server listen err:", err) return } //創建服務器gotoutine,負責從客戶端goroutine讀取粘包的數據,然后進行解析 for { conn, err := listener.Accept() if err != nil { fmt.Println("server accept err:", err) } //處理客戶端請求 go func(conn net.Conn) { //創建封包拆包對象dp dp := znet.NewDataPack() for { //1 先讀出流中的head部分 headData := make([]byte, dp.GetHeadLen()) _, err := io.ReadFull(conn, headData) //ReadFull 會把msg填充滿為止 if err != nil { fmt.Println("read head error") break } //將headData字節流 拆包到msg中 msgHead, err := dp.Unpack(headData) if err != nil { fmt.Println("server unpack err:", err) return } if msgHead.GetDataLen() > 0 { //msg 是有data數據的,需要再次讀取data數據 msg := msgHead.(*znet.Message) msg.Data = make([]byte, msg.GetDataLen()) //根據dataLen從io中讀取字節流 _, err := io.ReadFull(conn, msg.Data) if err != nil { fmt.Println("server unpack data err:", err) return } fmt.Println("==> Recv Msg: ID=", msg.Id, ", len=", msg.DataLen, ", data=", string(msg.Data)) } } }(conn) } } ``` > Client.go ```go package main import ( "fmt" "net" "zinx/znet" ) func main() { //客戶端goroutine,負責模擬粘包的數據,然后進行發送 conn, err := net.Dial("tcp", "127.0.0.1:7777") if err != nil { fmt.Println("client dial err:", err) return } //創建一個封包對象 dp dp := znet.NewDataPack() //封裝一個msg1包 msg1 := &znet.Message{ Id: 0, DataLen: 5, Data: []byte{'h', 'e', 'l', 'l', 'o'}, } sendData1, err := dp.Pack(msg1) if err != nil { fmt.Println("client pack msg1 err:", err) return } msg2 := &znet.Message{ Id: 1, DataLen: 7, Data: []byte{'w', 'o', 'r', 'l', 'd', '!', '!'}, } sendData2, err := dp.Pack(msg2) if err != nil { fmt.Println("client temp msg2 err:", err) return } //將sendData1,和 sendData2 拼接一起,組成粘包 sendData1 = append(sendData1, sendData2...) //向服務器端寫數據 conn.Write(sendData1) //客戶端阻塞 select {} } ``` 運行Server.go ``` go run Server.go ``` 運行Client.go ``` go run Client.go ``` 我們從服務端看到運行結果 ```bash $go run Server.go ==> Recv Msg: ID= 0 , len= 5 , data= hello ==> Recv Msg: ID= 1 , len= 7 , data= world!! ``` 我們成功的得到了客戶端發送的兩個包,并且成功的解析出來。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看