gtcp provides many convenient native methods for connecting and operating data. However, in most application scenarios, developers need to design their own data structures and perform packaging/unpacking. Since the TCP message protocol does not have message boundary protection, sticky packets can easily occur in complex network communication environments. Therefore, gtcp also provides a simple data protocol, making it easy for developers to interact with message packages without worrying about processing details, including packaging/unpacking, as all these complex logics have been handled by gtcp.

Simple Protocol

The gtcp module offers a simple, lightweight data interaction protocol with high efficiency. The protocol format is as follows:

  1. Data Length (16bit) | Data Field (variable length)
  1. Data Length: Default is 16-bit (2 bytes), used to identify the data length of the message body in bytes, not including its own 2 bytes;
  2. Data Field: Variable length. With data length, the maximum data length cannot exceed 0xFFFF = 65535 bytes = 64 KB;

The simple protocol is implemented by gtcp. If both the client’s and server’s developers use the gtcp module for communication, then there is no need to worry about protocol implementation, and they can focus on implementing/enclosing the Data field. If interfacing with other development languages is involved, it only requires implementation according to this protocol, as the simple protocol is very lightweight, leading to low interfacing costs.

TCP Object - Package - 图1tip

The Data field can also be empty, meaning no length at all.

Operation Methods

https://pkg.go.dev/github.com/gogf/gf/v2/net/gtcp

  1. type Conn
  2. func (c *Conn) SendPkg(data []byte, option ...PkgOption) error
  3. func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) error
  4. func (c *Conn) SendRecvPkg(data []byte, option ...PkgOption) ([]byte, error)
  5. func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) ([]byte, error)
  6. func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error)
  7. func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) ([]byte, error)

As seen, the message package methods are named by adding the Pkg keyword to the original basic connection operation methods for easy distinction.

Among them, the PkgOption data structure in the request parameter is as follows, used to define the message package receiving strategy:

  1. // Data reading options
  2. type PkgOption struct {
  3. HeaderSize int // Custom header size (default is 2 bytes, maximum cannot exceed 4 bytes)
  4. MaxDataSize int // (byte) Maximum package size for data reading, default maximum cannot exceed 2 bytes (65535 byte)
  5. Retry Retry // Failure retry policy
  6. }

Usage Examples

Example 1, Basic Usage

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/gogf/gf/v2/net/gtcp"
  5. "github.com/gogf/gf/v2/os/glog"
  6. "github.com/gogf/gf/v2/util/gconv"
  7. "time"
  8. )
  9. func main() {
  10. // Server
  11. go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
  12. defer conn.Close()
  13. for {
  14. data, err := conn.RecvPkg()
  15. if err != nil {
  16. fmt.Println(err)
  17. break
  18. }
  19. fmt.Println("receive:", data)
  20. }
  21. }).Run()
  22. time.Sleep(time.Second)
  23. // Client
  24. conn, err := gtcp.NewConn("127.0.0.1:8999")
  25. if err != nil {
  26. panic(err)
  27. }
  28. defer conn.Close()
  29. for i := 0; i < 10000; i++ {
  30. if err := conn.SendPkg([]byte(gconv.String(i))); err != nil {
  31. glog.Error(err)
  32. }
  33. time.Sleep(1*time.Second)
  34. }
  35. }

This example is quite simple. After execution, the output result is:

  1. receive: [48]
  2. receive: [49]
  3. receive: [50]
  4. receive: [51]
  5. ...

Example 2, Custom Data Structure

In most scenarios, we need to customize the data structure for the messages sent. Developers can use the Data field to transmit any message content.

Below is a simple example of a custom data structure for a client to report the current memory and CPU usage of the host node. In this example, the Data field uses JSON data format for customization, making data encoding/decoding easier.

TCP Object - Package - 图2tip

In practical scenarios, developers often need to customize package parsing protocols or adopt more general protobuf binary package encapsulation/parsing protocols.

  1. types/types.go

Data structure definition.

  1. package types
  2. import "github.com/gogf/gf/v2/frame/g"
  3. type NodeInfo struct {
  4. Cpu float32 // CPU percentage (%)
  5. Host string // Host name
  6. Ip g.Map // IP address information (possibly multiple)
  7. MemUsed int // Memory used (byte)
  8. MemTotal int // Total memory (byte)
  9. Time int // Reporting time (timestamp)
  10. }
  1. gtcp_monitor_server.go

Server.

  1. package main
  2. import (
  3. "encoding/json"
  4. "github.com/gogf/gf/v2/net/gtcp"
  5. "github.com/gogf/gf/v2/os/glog"
  6. "github.com/gogf/gf/.example/net/gtcp/pkg_operations/monitor/types"
  7. )
  8. func main() {
  9. // Server, receive client data and format it into a specified data structure, print
  10. gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
  11. defer conn.Close()
  12. for {
  13. data, err := conn.RecvPkg()
  14. if err != nil {
  15. if err.Error() == "EOF" {
  16. glog.Println("client closed")
  17. }
  18. break
  19. }
  20. info := &types.NodeInfo{}
  21. if err := json.Unmarshal(data, info); err != nil {
  22. glog.Errorf("invalid package structure: %s", err.Error())
  23. } else {
  24. glog.Println(info)
  25. conn.SendPkg([]byte("ok"))
  26. }
  27. }
  28. }).Run()
  29. }
  1. gtcp_monitor_client.go

Client.

  1. package main
  2. import (
  3. "encoding/json"
  4. "github.com/gogf/gf/v2/frame/g"
  5. "github.com/gogf/gf/v2/net/gtcp"
  6. "github.com/gogf/gf/v2/os/glog"
  7. "github.com/gogf/gf/v2/os/gtime"
  8. "github.com/gogf/gf/.example/net/gtcp/pkg_operations/monitor/types"
  9. )
  10. func main() {
  11. // Data reporting client
  12. conn, err := gtcp.NewConn("127.0.0.1:8999")
  13. if err != nil {
  14. panic(err)
  15. }
  16. defer conn.Close()
  17. // Format data fields using JSON
  18. info, err := json.Marshal(types.NodeInfo{
  19. Cpu : float32(66.66),
  20. Host : "localhost",
  21. Ip : g.Map {
  22. "etho" : "192.168.1.100",
  23. "eth1" : "114.114.10.11",
  24. },
  25. MemUsed : 15560320,
  26. MemTotal : 16333788,
  27. Time : int(gtime.Timestamp()),
  28. })
  29. if err != nil {
  30. panic(err)
  31. }
  32. // Use SendRecvPkg to send a message package and receive return
  33. if result, err := conn.SendRecvPkg(info); err != nil {
  34. if err.Error() == "EOF" {
  35. glog.Println("server closed")
  36. }
  37. } else {
  38. glog.Println(string(result))
  39. }
  40. }
  1. After execution

    • The client output result is:

      1. 2019-05-03 13:33:25.710 ok
    • The server output result is:

      1. 2019-05-03 13:33:25.710 &{66.66 localhost map[eth1:114.114.10.11 etho:192.168.1.100] 15560320 16333788 1556861605}
      2. 2019-05-03 13:33:25.710 client closed