基于TCP服务的TLV编解码

基于TCP服务的TLV编解码

一、实现思路

  • 实现一个tcp服务端和tcp客户端
  • 通过TLV方式编解码

TLV分别为:标签域(Tag),长度域(Length),内容域(Value)。

二、实现代码

service_test.go

复制代码
package server

import (
	"errors"
	"fmt"
	"github.com/sea-project/sea-pkg/util/conv"
	"io"
	"net"
	"testing"
)

const (
	TypeLen       = 1 // 2^8=256
	LenLen        = 2 // 2^16=65536
	ContentMaxLen = 1024 * 64
	BufLen        = 1 + 2 + 1024*64

	MsgText = uint8(66) // 文字信息
	MsgImg  = uint8(88) // 图片信息
)

func Test_TLVMain(t *testing.T) {
	listener, _ := net.Listen("tcp", "127.0.0.1:8888")
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println(err)
			continue
		}
		fmt.Println("A client connected :" + conn.RemoteAddr().String())
		go tcpPipe(conn)
	}
}

func tcpPipe(conn net.Conn) {
	bufData := make([]byte, BufLen)
	var err error
	var data []byte
	for {

		_, _ = io.ReadFull(conn, bufData)
		if len(bufData) == 0 {
			break
		}
		data, bufData, err = Unpack(bufData)
		if err != nil {
			conn.Close()
		}
		if len(data) == 0 {
			break
		}
		var msgType string
		if uint8(data[:1][0]) == 66 {
			msgType = "文字消息:"
		} else {
			msgType = "图片消息:"
		}

		fmt.Println(msgType + string(data[1:]))
	}
}

func Unpack(buf []byte) ([]byte, []byte, error) {
	// 取出消息类型
	t := uint8(buf[:1][0])
	if t != 66 && t != 88 {
		err := errors.New("消息类型错误")
		return nil, nil, err
	}
	// 取出消息长度
	l := conv.Bytes2uint16(buf[1:3])

	data := buf[0 : l+3]
	bufData := buf[l+3:]

	return data, bufData, nil
}

client_test.go

复制代码
package client

import (
	"bytes"
	"encoding/binary"
	"github.com/sea-project/sea-pkg/util/time"
	"net"
	"testing"
)

const (
	TypeLen       = 1 // 2^8=256
	LenLen        = 2 // 2^16=65536
	ContentMaxLen = 1024 * 64
	BufLen        = 1 + 2 + 1024*64

	MsgText = uint8(66) // 文字信息
	MsgImg  = uint8(88) // 图片信息
)

func Test_TLVMain2(t *testing.T) {
	conn, _ := net.Dial("tcp", "127.0.0.1:8888")
	for i := 0; i < 100; i++ {

		currTime := time.GetUnixToFormatString(time.CurrentSecond(), "2006-01-02 15:04:05")
		data := []byte(currTime + " Hello World! \n")

		data = Pack(data)

		conn.Write(data)
	}

}

func Pack(msg []byte) []byte {
	var t uint8
	var l uint16

	if time.CurrentMilliSecond()%2 == 0 {
		t = 66
	} else {
		t = 88
	}
	l = uint16(len(msg))

	dataBuff := bytes.NewBuffer([]byte{})
	if err := binary.Write(dataBuff, binary.BigEndian, t); err != nil {
		return nil
	}
	if err := binary.Write(dataBuff, binary.BigEndian, l); err != nil {
		return nil
	}
	if err := binary.Write(dataBuff, binary.BigEndian, msg); err != nil {
		return nil
	}
	return dataBuff.Bytes()
}
相关推荐
暖馒2 小时前
Modbus应用层协议的深度剖析
网络·网络协议·c#·wpf·智能硬件
yunfuuwqi3 小时前
OpenClaw✅真·喂饭级教程:2026年OpenClaw(原Moltbot)一键部署+接入飞书最佳实践
运维·服务器·网络·人工智能·飞书·京东云
迎仔4 小时前
C-算力中心网络隔离实施方法:怎么搞?
运维·网络
代码游侠4 小时前
C语言核心概念复习——网络协议与TCP/IP
linux·运维·服务器·网络·算法
枷锁—sha4 小时前
【SRC】SQL注入WAF 绕过应对策略(二)
网络·数据库·python·sql·安全·网络安全
Zach_yuan5 小时前
深入浅出 JSONCpp
linux·服务器·网络·c++
迎仔7 小时前
B-算力中心网络隔离的必要性:为什么必须隔离?
网络
野指针YZZ7 小时前
一键配置RK3588网络与SSH远程连接
网络·ssh·rk3588
迎仔8 小时前
10-网络安全监控与事件响应:数字世界的智能监控与应急系统
网络·安全·web安全
上海合宙LuatOS8 小时前
LuatOS核心库API——【audio 】
java·网络·单片机·嵌入式硬件·物联网·音视频·硬件工程