通信开发进阶

    使用示例

    我们通过一个完成的示例来说明一下如何在程序中实现异步全双工通信,完成示例代码位于:https://github.com/gogf/gf/tree/master/geg/net/gtcp/pkg_operations/common

    1. types/types.go

      考虑到简化测试代码复杂度,因此这里使用JSON数据格式来传递数据。在一些对于消息包大小比较严格的场景中,数据字段可以自行按照二进制进行封装解析设计。此外,需要注意的是,即使使用JSON数据格式,其中的Act字段往往定义常量来实现,大部分场景中使用uint8类型即可,以减小消息包大小,这里偷一下懒,直接使用字符串,以便演示。

    2. funcs/funcs.go

      自定义数据格式的发送/获取定义,便于数据结构编码/解析。

      1. package funcs
      2. import (
      3. "encoding/json"
      4. "fmt"
      5. "github.com/gogf/gf/g/net/gtcp"
      6. "github.com/gogf/gf/geg/net/gtcp/pkg_operations/common/types"
      7. )
      8. // 自定义格式发送消息包
      9. func SendPkg(conn *gtcp.Conn, act string, data...string) error {
      10. s := ""
      11. if len(data) > 0 {
      12. s = data[0]
      13. }
      14. msg, err := json.Marshal(types.Msg{
      15. Act : act,
      16. Data : s,
      17. })
      18. if err != nil {
      19. panic(err)
      20. }
      21. return conn.SendPkg(msg)
      22. }
      23. func RecvPkg(conn *gtcp.Conn) (msg *types.Msg, err error) {
      24. if data, err := conn.RecvPkg(); err != nil {
      25. return nil, err
      26. } else {
      27. msg = &types.Msg{}
      28. err = json.Unmarshal(data, msg)
      29. return nil, fmt.Errorf("invalid package structure: %s", err.Error())
      30. }
      31. return msg, err
      32. }
      33. }
    3. 通信服务端。在该示例中,服务端并不主动断开连接,而是在10秒后向客户端发送doexit消息,通知客户端主动断开连接,以结束示例。

    4. gtcp_common_client.go

      通信客户端,可以看到代码结构和服务端差不多,数据获取独立处于for循环中,每个业务逻辑发送消息包时直接使用SendPkg方法进行发送。

      客户端连接10秒后,服务端会给客户端发送doexit消息,客户端收到该消息后便主动断开连接,长连接结束。

      1. package main
      2. import (
      3. "github.com/gogf/gf/g/net/gtcp"
      4. "github.com/gogf/gf/g/os/glog"
      5. "github.com/gogf/gf/g/os/gtimer"
      6. "github.com/gogf/gf/geg/net/gtcp/pkg_operations/common/funcs"
      7. "github.com/gogf/gf/geg/net/gtcp/pkg_operations/common/types"
      8. "time"
      9. )
      10. func main() {
      11. conn, err := gtcp.NewConn("127.0.0.1:8999")
      12. if err != nil {
      13. panic(err)
      14. }
      15. defer conn.Close()
      16. gtimer.SetInterval(time.Second, func() {
      17. if err := funcs.SendPkg(conn, "heartbeat"); err != nil {
      18. panic(err)
      19. }
      20. })
      21. // 测试消息, 3秒后向服务端发送hello消息
      22. gtimer.SetTimeout(3*time.Second, func() {
      23. if err := funcs.SendPkg(conn, "hello", "My name's John!"); err != nil {
      24. panic(err)
      25. }
      26. for {
      27. msg, err := funcs.RecvPkg(conn)
      28. if err != nil {
      29. if err.Error() == "EOF" {
      30. glog.Println("server closed")
      31. }
      32. break
      33. }
      34. switch msg.Act {
      35. case "hello": onServerHello(conn, msg)
      36. case "doexit": onServerDoExit(conn, msg)
      37. case "heartbeat": onServerHeartBeat(conn, msg)
      38. default:
      39. glog.Errorfln("invalid message: %v", msg)
      40. break
      41. }
      42. }
      43. }
      44. func onServerHello(conn *gtcp.Conn, msg *types.Msg) {
      45. glog.Printfln("hello response message from [%s]: %s", conn.RemoteAddr().String(), msg.Data)
      46. }
      47. func onServerHeartBeat(conn *gtcp.Conn, msg *types.Msg) {
      48. glog.Printfln("heartbeat from [%s]", conn.RemoteAddr().String())
      49. }
      50. func onServerDoExit(conn *gtcp.Conn, msg *types.Msg) {
      51. glog.Printfln("exit command from [%s]", conn.RemoteAddr().String())
      52. conn.Close()
      53. }
    5. 执行后
      • 服务端输出结果
      • 客户端输出结果