Golang实现之TCP长连接-------服务端和客户端

一、数据包的数据结构 (所有字段采用大端序)

帧头

帧长度(头至尾)

帧类型

帧数据

帧尾

1字节

4字节

2字节

1024字节

1字节

byte

int

short

string

byte

0xC8

0xC9

二、Server端 实现代码

1、main.go

func main() {

	logconfig.InitLogger()//初始化日志库,日志库实现可以查阅: 
   https://blog.csdn.net/banzhuantuqiang/article/details/131403454


	//开启tcp server
	logconfig.SugarLogger.Info("server端启动中...")
	ip := "0.0.0.0"
	port := 10302
	

	go func() {
		
		server.Start(ip, port)
	}()


	logconfig.SugarLogger.Info("server端启动完成")

	for {
		logconfig.SugarLogger.Info("server端主进程活跃")
		time.Sleep(5 * time.Second)
	}

}

2、server.go

//启动TCP服务端
func Start(ip string, port int) (bool, error) {
var ClinetConn net.Conn = nil
	//1:启用监听
	listener, err := net.Listen("tcp", ip+":"+strconv.Itoa(port))
	//连接失败处理
	if err != nil {
		logconfig.SugarLogger.Infof("启动服务失败,err:%v", err)
		return false, err
	}

	//程序退出时释放端口
	defer func() {
		listener.Close()
		logconfig.SugarLogger.Error("服务的退出")
	}()


	for {
		coon, err := listener.Accept() //2.建立连接
		//判断是代理连接还是ssh连接
		reader := bufio.NewReader(coon)
	
		
			if ClinetConn != nil {
				ClinetConn.Close()
			}
			ClinetConn = coon
			if err != nil {
				logconfig.SugarLogger.Errorf("接收客户连接失败,err:%v", err)
				continue
			}

			logconfig.SugarLogger.Infof("接收客户连接成功,%s", coon.RemoteAddr().String())
			
			//启动一个goroutine处理客户端连接
			go process(coon, reader)
	}
}

func process(coon net.Conn, reader *bufio.Reader) {
	defer func() {
		coon.Close()
		logconfig.SugarLogger.Infof("客户连接断开,%s", coon.RemoteAddr().String())
	}()
	for {
		msg, err := protocol.Decode(reader, coon)
		if err != nil {
			logconfig.SugarLogger.Infof("decode失败,err:%v", err)
			if msg.Type == 1 {
				continue
			} else {
				logconfig.SugarLogger.Errorf("客户端连接处理异常......")
				return
			}
		}
		logconfig.SugarLogger.Infof("收到客户端%s的消息:%v", coon.RemoteAddr().String(), msg)
		//TODO 解析数据和回应消息,参照下面response.go
        //这里另起一个线程去解析数据,一般是json数据
	}
}

3、protocol.go

const HEAD byte = 0xC8
const TAIL byte = 0xC9


func Encode(message Msg) ([]byte, error) {
	var pkg = new(bytes.Buffer)
	// 写入消息头
	err := binary.Write(pkg, binary.BigEndian, message.Head)
	if err != nil {
		return nil, err
	}
	// 写入长度
	err = binary.Write(pkg, binary.BigEndian, message.Length)
	if err != nil {
		return nil, err
	}
	// 写入类型
	err = binary.Write(pkg, binary.BigEndian, message.Type)
	if err != nil {
		return nil, err
	}
	err = binary.Write(pkg, binary.BigEndian, []byte(message.Data))
	if err != nil {
		return nil, err
	}
	err = binary.Write(pkg, binary.BigEndian, message.Tail)
	if err != nil {
		return nil, err
	}
	return pkg.Bytes(), nil
}


// 解码(这里考虑了粘包和分包问题)
func Decode(reader *bufio.Reader, conn net.Conn) (Msg, error) {
	//|帧头    |帧长度(头至尾)   	|帧类型  	|帧数据    	|帧尾
	//|1字节   |4字节    			|2字节   	|1024字节  	|1字节
	//|byte   |int     			|short   	|string   	|byte
	//|0xC8   |        			|        	|         	|0xC9

	for {
		headbyte, headError := reader.ReadByte()
		if headError != nil {
			return Msg{}, headError
		} else if headbyte == HEAD {
			//已读取到正确头
			break
		} else {
			logconfig.SugarLogger.Infof("读到错误字节:%v 错误的连接地址:%s", headbyte, conn.RemoteAddr().String())
		}
	}
	// 读消息长度,不移动位置
	lengthByte, _ := reader.Peek(4)
	lengthBuff := bytes.NewBuffer(lengthByte)
	var length int32
	//将长度buff转换为 int32,大端序
	err := binary.Read(lengthBuff, binary.BigEndian, &length)
	if err != nil {
		return Msg{Type: 1}, err
	}
	//如果消息体超过 4096(默认长度)
	var pack []byte
	if length > 4096 {
		pack = make([]byte, 0, int(length-1))
		readableLength := length - 1
		for {
			if readableLength < 4096 {
				slice := make([]byte, readableLength)
				_, err = reader.Read(slice)
				pack = append(pack, slice...)
				break
			}
			slice := make([]byte, int32(reader.Buffered()))
			_, err = reader.Read(slice)
			pack = append(pack, slice...)
			//更新可读长度
			readableLength = readableLength - int32(len(slice))
		}
		// buffer返回缓冲中现有的可读的字节数,2+length+1表示帧类型+数据长度+帧尾
	} else if length < 4096 && int32(reader.Buffered()) < length-1 {
		//退回已读取的帧头
		reader.UnreadByte()
		return Msg{Type: 1}, errors.New("数据长度不足")
	} else {
		// 读取剩余帧内容
		pack = make([]byte, int(length-1))
		_, err = reader.Read(pack)
		if err != nil {
			return Msg{Type: 1}, err
		}
	}

	typeBuff := bytes.NewBuffer(pack[4:6])
	var msgType int16
	msgTypeErr := binary.Read(typeBuff, binary.BigEndian, &msgType)
	if msgTypeErr != nil {
		return Msg{Type: 1}, msgTypeErr
	}
	data := string(pack[6 : len(pack)-1])
	tail := pack[len(pack)-1]
	if tail != TAIL {
		reader.UnreadByte()
		return Msg{Type: 1}, errors.New("帧尾错误,丢弃已读取的字节")
	}
	msg := Msg{Head: HEAD, Length: length, Type: msgType, Data: data, Tail: TAIL}
	return msg, nil
}

type Msg struct {
	Head   byte
	Length int32
	Type   int16
	Data   string
	Tail   byte
}

4、response.go

func tcpReturn(coon net.Conn, result *dto.Result, msgType int16) {
	marshal, _ := json.Marshal(result)//这里根据定义的消息体解析成json字符串
	msg := protocol.Msg{protocol.HEAD, 8 + int32(len(marshal)), msgType, string(marshal), protocol.TAIL}
	encode, _ := protocol.Encode(msg)
	coon.Write(encode)
	logconfig.SugarLogger.Infof("结束消息处理,tpc连接:%s,回复结果:%s", coon.RemoteAddr().String(), string(encode))
}

5、result.go

type Result struct {
	DataType string `json:"data_type"`
	// 消息标识
	CallBackKey string `json:"call_back_key"`
	// 状态码
	Status string `json:"status"`
	// 返回描述
	Message string `json:"message"`
	// 消息体
	Data interface{} `json:"data"`
}

三、Client端 实现代码

package main

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"net"
	"time"
)
const HEAD byte = 0xC8
const TAIL byte = 0xC9
func main() {
	
		conn, err := net.Dial("tcp", "127.0.0.1:10301")
		if err != nil {
			fmt.Println("连接服务端失败,err:", err)
			return
		}
		conn.SetReadDeadline(time.Now().Add(100 * time.Second))

		

		strJosn:="66666"//这里写自己的json字符串
		//Type 0x01  0x02.....
		message := Msg{HEAD, 8 + int32(len(strJosn)), 0x10, string(strJosn), TAIL} // 板卡ROM重置状态查询

		b, err := Encode(message)
		if err != nil {
			fmt.Println("Encode失败,err:", err)
		}
		_, error := conn.Write(b)
		if error != nil {
			fmt.Println("发送失败,err:", error)
			return
		}
		fmt.Println("发送成功...,msg:", b)
		fmt.Println("发送成功...,msg:", message)
	    parseServerResponseMesage(conn)
	
}

//服务端返回消息
func parseServerResponseMesage(coon net.Conn) {
	for {
		dataByte := make([]byte, 4096)
		n, _ := coon.Read(dataByte)
		bytes := dataByte[0:n]
		fmt.Println("收到服务端消息:", string(bytes))
	}
}


type Msg struct {
	Head   byte
	Length int32
	Type   int16
	Data   string
	Tail   byte
}

func Encode(message Msg) ([]byte, error) {
	var pkg = new(bytes.Buffer)
	// 写入消息头
	err := binary.Write(pkg, binary.BigEndian, message.Head)
	if err != nil {
		return nil, err
	}
	// 写入长度
	err = binary.Write(pkg, binary.BigEndian, message.Length)
	if err != nil {
		return nil, err
	}
	// 写入类型
	err = binary.Write(pkg, binary.BigEndian, message.Type)
	if err != nil {
		return nil, err
	}
	err = binary.Write(pkg, binary.BigEndian, []byte(message.Data))
	if err != nil {
		return nil, err
	}
	err = binary.Write(pkg, binary.BigEndian, message.Tail)
	if err != nil {
		return nil, err
	}
	return pkg.Bytes(), nil
}


猜你喜欢

转载自blog.csdn.net/banzhuantuqiang/article/details/131402492