缘起

最近阅读<> (刘金亮, 2021.1)
本系列笔记拟采用golang练习之

案例需求(聊天服务器)

  • 用户可以连接到服务器。
  • 用户可以设定自己的用户名。
  • 用户可以向服务器发送消息,同时服务器也会向其他用户广播该消息。

目标

  • 定义通信协议, 包括信令定义, 编解码实现
  • 实现聊天客户端(时间有限, 后续实现聊天服务端并测试)

设计

  • IMsg: 定义消息接口, 以及相关消息的实现. 为方便任意消息内容的解码, 消息传输时, 采用base64转码
  • IMsgDecoder: 定义消息解码器及其实现
  • IChatClient: 定义聊天客户端接口
  • tChatClient: 聊天客户端, 实现IChatClient接口
  • IChatServer: 尚未完成
  • tChatServer: 尚未完成

IMsg.go

定义消息接口, 以及相关消息的实现. 为方便任意消息内容的解码, 消息传输时, 采用base64转码

  1. package chat_server
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. )
  6. type IMsg interface {
  7. Encode() string
  8. }
  9. type NameMsg struct {
  10. Name string
  11. }
  12. func (me *NameMsg) Encode() string {
  13. return fmt.Sprintf("NAME %s", base64.StdEncoding.EncodeToString([]byte(me.Name)))
  14. }
  15. type ChatMsg struct {
  16. Name string
  17. Words string
  18. }
  19. func (me *ChatMsg) Encode() string {
  20. return fmt.Sprintf("CHAT %s %s",
  21. base64.StdEncoding.EncodeToString([]byte(me.Name)),
  22. base64.StdEncoding.EncodeToString([]byte(me.Words)),
  23. )
  24. }

IMsgDecoder.go

定义消息解码器及其实现

  1. package chat_server
  2. import (
  3. "encoding/base64"
  4. "strings"
  5. )
  6. type IMsgDecoder interface {
  7. Decode(line string) (bool, IMsg)
  8. }
  9. type tMsgDecoder struct {
  10. }
  11. func (me *tMsgDecoder) Decode(line string) (bool, IMsg) {
  12. items := strings.Split(line, " ")
  13. size := len(items)
  14. if items[0] == "NAME" && size == 2 {
  15. name, err := base64.StdEncoding.DecodeString(items[1])
  16. if err != nil {
  17. return false, nil
  18. }
  19. return true, &NameMsg{
  20. Name: string(name),
  21. }
  22. }
  23. if items[0] == "CHAT" && size == 3 {
  24. name, err := base64.StdEncoding.DecodeString(items[1])
  25. if err != nil {
  26. return false, nil
  27. }
  28. words, err := base64.StdEncoding.DecodeString(items[2])
  29. if err != nil {
  30. return false, nil
  31. }
  32. return true, &ChatMsg{
  33. Name: string(name),
  34. Words: string(words),
  35. }
  36. }
  37. return false, nil
  38. }
  39. var MsgDecoder = &tMsgDecoder{}

IChatClient.go

定义聊天客户端接口

  1. package chat_server
  2. type IChatClient interface {
  3. Dial(address string) error
  4. Send(msg IMsg)
  5. RecvHandler(handler RecvFunc)
  6. Close()
  7. }
  8. type RecvFunc func(msg IMsg)

tChatClient.go

聊天客户端, 实现IChatClient接口

  1. package chat_server
  2. import (
  3. "bufio"
  4. "net"
  5. "sync/atomic"
  6. )
  7. type tChatClient struct {
  8. conn net.Conn
  9. closeFlag int32
  10. closeChan chan bool
  11. sendChan chan IMsg
  12. name string
  13. sendLogs []IMsg
  14. recvLogs []IMsg
  15. recvHandler RecvFunc
  16. }
  17. func DialChatClient(address string) (error, IChatClient) {
  18. it := &tChatClient{
  19. conn: nil,
  20. closeFlag: 0,
  21. closeChan: make(chan bool),
  22. sendChan: make(chan IMsg),
  23. name: "anonymous",
  24. sendLogs: []IMsg{},
  25. recvLogs: []IMsg{},
  26. }
  27. e := it.Dial(address)
  28. if e != nil {
  29. return e, nil
  30. }
  31. return nil, it
  32. }
  33. func (me *tChatClient) Dial(address string) error {
  34. c, e := net.Dial("tcp", address)
  35. if e != nil {
  36. return e
  37. }
  38. me.conn = c
  39. go me.beginWrite()
  40. go me.beginRead()
  41. return nil
  42. }
  43. func (me *tChatClient) isClosed() bool {
  44. return me.closeFlag != 0
  45. }
  46. func (me *tChatClient) isNotClosed() bool {
  47. return !me.isClosed()
  48. }
  49. func (me *tChatClient) Send(msg IMsg) {
  50. if me.isNotClosed() {
  51. me.sendChan <- msg
  52. }
  53. }
  54. func (me *tChatClient) RecvHandler(handler RecvFunc) {
  55. if me.isNotClosed() {
  56. me.recvHandler = handler
  57. }
  58. }
  59. func (me *tChatClient) Close() {
  60. if me.isNotClosed() {
  61. me.closeConn()
  62. }
  63. }
  64. func (me *tChatClient) beginWrite() {
  65. writer := bufio.NewWriter(me.conn)
  66. newline := '\n'
  67. for {
  68. select {
  69. case <- me.closeChan:
  70. _ = me.conn.Close()
  71. me.closeFlag = 2
  72. return
  73. case msg := <- me.sendChan:
  74. _,e := writer.WriteString(msg.Encode())
  75. if e != nil {
  76. me.closeConn()
  77. } else {
  78. _,e = writer.WriteRune(newline)
  79. if e != nil {
  80. me.closeConn()
  81. }
  82. }
  83. }
  84. }
  85. }
  86. func (me *tChatClient) beginRead() {
  87. reader := bufio.NewReader(me.conn)
  88. for me.isNotClosed() {
  89. line, _, err := reader.ReadLine()
  90. if err != nil {
  91. break
  92. }
  93. ok, msg := MsgBuilder.Build(string(line))
  94. if ok {
  95. fn := me.recvHandler
  96. if fn != nil {
  97. fn(msg)
  98. }
  99. }
  100. }
  101. }
  102. func (me *tChatClient) closeConn() {
  103. if atomic.CompareAndSwapInt32(&me.closeFlag, 0, 1) {
  104. me.closeChan <- true
  105. }
  106. }

(未完待续)