当前位置:   article > 正文

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

golang tcp server

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

帧头

帧长度(头至尾)

帧类型

帧数据

帧尾

1字节

4字节

2字节

1024字节

1字节

byte

int

short

string

byte

0xC8

0xC9

二、Server端 实现代码

1、main.go

  1. func main() {
  2. logconfig.InitLogger()//初始化日志库,日志库实现可以查阅:
  3. https://blog.csdn.net/banzhuantuqiang/article/details/131403454
  4. //开启tcp server
  5. logconfig.SugarLogger.Info("server端启动中...")
  6. ip := "0.0.0.0"
  7. port := 10302
  8. go func() {
  9. server.Start(ip, port)
  10. }()
  11. logconfig.SugarLogger.Info("server端启动完成")
  12. for {
  13. logconfig.SugarLogger.Info("server端主进程活跃")
  14. time.Sleep(5 * time.Second)
  15. }
  16. }

2、server.go

  1. //启动TCP服务端
  2. func Start(ip string, port int) (bool, error) {
  3. var ClinetConn net.Conn = nil
  4. //1:启用监听
  5. listener, err := net.Listen("tcp", ip+":"+strconv.Itoa(port))
  6. //连接失败处理
  7. if err != nil {
  8. logconfig.SugarLogger.Infof("启动服务失败,err:%v", err)
  9. return false, err
  10. }
  11. //程序退出时释放端口
  12. defer func() {
  13. listener.Close()
  14. logconfig.SugarLogger.Error("服务的退出")
  15. }()
  16. for {
  17. coon, err := listener.Accept() //2.建立连接
  18. //判断是代理连接还是ssh连接
  19. reader := bufio.NewReader(coon)
  20. if ClinetConn != nil {
  21. ClinetConn.Close()
  22. }
  23. ClinetConn = coon
  24. if err != nil {
  25. logconfig.SugarLogger.Errorf("接收客户连接失败,err:%v", err)
  26. continue
  27. }
  28. logconfig.SugarLogger.Infof("接收客户连接成功,%s", coon.RemoteAddr().String())
  29. //启动一个goroutine处理客户端连接
  30. go process(coon, reader)
  31. }
  32. }
  33. func process(coon net.Conn, reader *bufio.Reader) {
  34. defer func() {
  35. coon.Close()
  36. logconfig.SugarLogger.Infof("客户连接断开,%s", coon.RemoteAddr().String())
  37. }()
  38. for {
  39. msg, err := protocol.Decode(reader, coon)
  40. if err != nil {
  41. logconfig.SugarLogger.Infof("decode失败,err:%v", err)
  42. if msg.Type == 1 {
  43. continue
  44. } else {
  45. logconfig.SugarLogger.Errorf("客户端连接处理异常......")
  46. return
  47. }
  48. }
  49. logconfig.SugarLogger.Infof("收到客户端%s的消息:%v", coon.RemoteAddr().String(), msg)
  50. //TODO 解析数据和回应消息,参照下面response.go
  51. //这里另起一个线程去解析数据,一般是json数据
  52. }
  53. }

3、protocol.go

  1. const HEAD byte = 0xC8
  2. const TAIL byte = 0xC9
  3. func Encode(message Msg) ([]byte, error) {
  4. var pkg = new(bytes.Buffer)
  5. // 写入消息头
  6. err := binary.Write(pkg, binary.BigEndian, message.Head)
  7. if err != nil {
  8. return nil, err
  9. }
  10. // 写入长度
  11. err = binary.Write(pkg, binary.BigEndian, message.Length)
  12. if err != nil {
  13. return nil, err
  14. }
  15. // 写入类型
  16. err = binary.Write(pkg, binary.BigEndian, message.Type)
  17. if err != nil {
  18. return nil, err
  19. }
  20. err = binary.Write(pkg, binary.BigEndian, []byte(message.Data))
  21. if err != nil {
  22. return nil, err
  23. }
  24. err = binary.Write(pkg, binary.BigEndian, message.Tail)
  25. if err != nil {
  26. return nil, err
  27. }
  28. return pkg.Bytes(), nil
  29. }
  30. // 解码(这里考虑了粘包和分包问题)
  31. func Decode(reader *bufio.Reader, conn net.Conn) (Msg, error) {
  32. //|帧头 |帧长度(头至尾) |帧类型 |帧数据 |帧尾
  33. //|1字节 |4字节 |2字节 |1024字节 |1字节
  34. //|byte |int |short |string |byte
  35. //|0xC8 | | | |0xC9
  36. for {
  37. headbyte, headError := reader.ReadByte()
  38. if headError != nil {
  39. return Msg{}, headError
  40. } else if headbyte == HEAD {
  41. //已读取到正确头
  42. break
  43. } else {
  44. logconfig.SugarLogger.Infof("读到错误字节:%v 错误的连接地址:%s", headbyte, conn.RemoteAddr().String())
  45. }
  46. }
  47. // 读消息长度,不移动位置
  48. lengthByte, _ := reader.Peek(4)
  49. lengthBuff := bytes.NewBuffer(lengthByte)
  50. var length int32
  51. //将长度buff转换为 int32,大端序
  52. err := binary.Read(lengthBuff, binary.BigEndian, &length)
  53. if err != nil {
  54. return Msg{Type: 1}, err
  55. }
  56. //如果消息体超过 4096(默认长度)
  57. var pack []byte
  58. if length > 4096 {
  59. pack = make([]byte, 0, int(length-1))
  60. readableLength := length - 1
  61. for {
  62. if readableLength < 4096 {
  63. slice := make([]byte, readableLength)
  64. _, err = reader.Read(slice)
  65. pack = append(pack, slice...)
  66. break
  67. }
  68. slice := make([]byte, int32(reader.Buffered()))
  69. _, err = reader.Read(slice)
  70. pack = append(pack, slice...)
  71. //更新可读长度
  72. readableLength = readableLength - int32(len(slice))
  73. }
  74. // buffer返回缓冲中现有的可读的字节数,2+length+1表示帧类型+数据长度+帧尾
  75. } else if length < 4096 && int32(reader.Buffered()) < length-1 {
  76. //退回已读取的帧头
  77. reader.UnreadByte()
  78. return Msg{Type: 1}, errors.New("数据长度不足")
  79. } else {
  80. // 读取剩余帧内容
  81. pack = make([]byte, int(length-1))
  82. _, err = reader.Read(pack)
  83. if err != nil {
  84. return Msg{Type: 1}, err
  85. }
  86. }
  87. typeBuff := bytes.NewBuffer(pack[4:6])
  88. var msgType int16
  89. msgTypeErr := binary.Read(typeBuff, binary.BigEndian, &msgType)
  90. if msgTypeErr != nil {
  91. return Msg{Type: 1}, msgTypeErr
  92. }
  93. data := string(pack[6 : len(pack)-1])
  94. tail := pack[len(pack)-1]
  95. if tail != TAIL {
  96. reader.UnreadByte()
  97. return Msg{Type: 1}, errors.New("帧尾错误,丢弃已读取的字节")
  98. }
  99. msg := Msg{Head: HEAD, Length: length, Type: msgType, Data: data, Tail: TAIL}
  100. return msg, nil
  101. }
  102. type Msg struct {
  103. Head byte
  104. Length int32
  105. Type int16
  106. Data string
  107. Tail byte
  108. }

4、response.go

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

5、result.go

  1. type Result struct {
  2. DataType string `json:"data_type"`
  3. // 消息标识
  4. CallBackKey string `json:"call_back_key"`
  5. // 状态码
  6. Status string `json:"status"`
  7. // 返回描述
  8. Message string `json:"message"`
  9. // 消息体
  10. Data interface{} `json:"data"`
  11. }

三、Client端 实现代码

  1. package main
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "fmt"
  6. "net"
  7. "time"
  8. )
  9. const HEAD byte = 0xC8
  10. const TAIL byte = 0xC9
  11. func main() {
  12. conn, err := net.Dial("tcp", "127.0.0.1:10301")
  13. if err != nil {
  14. fmt.Println("连接服务端失败,err:", err)
  15. return
  16. }
  17. conn.SetReadDeadline(time.Now().Add(100 * time.Second))
  18. strJosn:="66666"//这里写自己的json字符串
  19. //Type 0x01 0x02.....
  20. message := Msg{HEAD, 8 + int32(len(strJosn)), 0x10, string(strJosn), TAIL} // 板卡ROM重置状态查询
  21. b, err := Encode(message)
  22. if err != nil {
  23. fmt.Println("Encode失败,err:", err)
  24. }
  25. _, error := conn.Write(b)
  26. if error != nil {
  27. fmt.Println("发送失败,err:", error)
  28. return
  29. }
  30. fmt.Println("发送成功...,msg:", b)
  31. fmt.Println("发送成功...,msg:", message)
  32. parseServerResponseMesage(conn)
  33. }
  34. //服务端返回消息
  35. func parseServerResponseMesage(coon net.Conn) {
  36. for {
  37. dataByte := make([]byte, 4096)
  38. n, _ := coon.Read(dataByte)
  39. bytes := dataByte[0:n]
  40. fmt.Println("收到服务端消息:", string(bytes))
  41. }
  42. }
  43. type Msg struct {
  44. Head byte
  45. Length int32
  46. Type int16
  47. Data string
  48. Tail byte
  49. }
  50. func Encode(message Msg) ([]byte, error) {
  51. var pkg = new(bytes.Buffer)
  52. // 写入消息头
  53. err := binary.Write(pkg, binary.BigEndian, message.Head)
  54. if err != nil {
  55. return nil, err
  56. }
  57. // 写入长度
  58. err = binary.Write(pkg, binary.BigEndian, message.Length)
  59. if err != nil {
  60. return nil, err
  61. }
  62. // 写入类型
  63. err = binary.Write(pkg, binary.BigEndian, message.Type)
  64. if err != nil {
  65. return nil, err
  66. }
  67. err = binary.Write(pkg, binary.BigEndian, []byte(message.Data))
  68. if err != nil {
  69. return nil, err
  70. }
  71. err = binary.Write(pkg, binary.BigEndian, message.Tail)
  72. if err != nil {
  73. return nil, err
  74. }
  75. return pkg.Bytes(), nil
  76. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/753919
推荐阅读
相关标签
  

闽ICP备14008679号