乐趣区

关于golang:手撸golang-GO与微服务-ChatServer之1

缘起

最近浏览 <<Go 微服务实战 >> (刘金亮, 2021.1)
本系列笔记拟采纳 golang 练习之

案例需要 (聊天服务器)

  • 用户能够连贯到服务器。
  • 用户能够设定本人的用户名。
  • 用户能够向服务器发送音讯,同时服务器也会向其余用户播送该音讯。

指标

  • 定义通信协议, 包含信令定义, 编解码实现
  • 实现聊天客户端 (工夫无限, 后续实现聊天服务端并测试)

设计

  • IMsg: 定义音讯接口, 以及相干音讯的实现. 为不便任意音讯内容的解码, 音讯传输时, 采纳 base64 转码
  • IMsgDecoder: 定义音讯解码器及其实现
  • IChatClient: 定义聊天客户端接口
  • tChatClient: 聊天客户端, 实现 IChatClient 接口
  • IChatServer: 尚未实现
  • tChatServer: 尚未实现

IMsg.go

定义音讯接口, 以及相干音讯的实现. 为不便任意音讯内容的解码, 音讯传输时, 采纳 base64 转码

package chat_server

import (
    "encoding/base64"
    "fmt"
)

type IMsg interface {Encode() string
}

type NameMsg struct {Name string}

func (me *NameMsg) Encode() string {return fmt.Sprintf("NAME %s", base64.StdEncoding.EncodeToString([]byte(me.Name)))
}

type ChatMsg struct {
    Name string
    Words string
}

func (me *ChatMsg) Encode() string {
    return fmt.Sprintf("CHAT %s %s",
        base64.StdEncoding.EncodeToString([]byte(me.Name)),
        base64.StdEncoding.EncodeToString([]byte(me.Words)),
    )
}

IMsgDecoder.go

定义音讯解码器及其实现

package chat_server

import (
    "encoding/base64"
    "strings"
)


type IMsgDecoder interface {Decode(line string) (bool, IMsg)
}

type tMsgDecoder struct {
}

func (me *tMsgDecoder) Decode(line string) (bool, IMsg) {items := strings.Split(line, " ")
    size := len(items)

    if items[0] == "NAME" && size == 2 {name, err := base64.StdEncoding.DecodeString(items[1])
        if err != nil {return false, nil}

        return true, &NameMsg{Name: string(name),
        }
    }

    if items[0] == "CHAT" && size == 3 {name, err := base64.StdEncoding.DecodeString(items[1])
        if err != nil {return false, nil}

        words, err := base64.StdEncoding.DecodeString(items[2])
        if err != nil {return false, nil}

        return true, &ChatMsg{Name: string(name),
            Words: string(words),
        }
    }

    return false, nil
}


var MsgDecoder = &tMsgDecoder{}

IChatClient.go

定义聊天客户端接口

package chat_server

type IChatClient interface {Dial(address string) error
    Send(msg IMsg)
    RecvHandler(handler RecvFunc)
    Close()}

type RecvFunc func(msg IMsg)

tChatClient.go

聊天客户端, 实现 IChatClient 接口

package chat_server

import (
    "bufio"
    "net"
    "sync/atomic"
)

type tChatClient struct {
    conn net.Conn
    closeFlag int32

    closeChan chan bool
    sendChan chan IMsg

    name string
    sendLogs []IMsg
    recvLogs []IMsg
    recvHandler RecvFunc
}

func DialChatClient(address string) (error, IChatClient) {
    it := &tChatClient{
        conn: nil,
        closeFlag: 0,

        closeChan: make(chan bool),
        sendChan: make(chan IMsg),

        name: "anonymous",
        sendLogs: []IMsg{},
        recvLogs: []IMsg{},
    }

    e := it.Dial(address)
    if e != nil {return e, nil}

    return nil, it
}

func (me *tChatClient) Dial(address string) error {c, e := net.Dial("tcp", address)
    if e != nil {return e}
    me.conn = c

    go me.beginWrite()
    go me.beginRead()

    return nil
}


func (me *tChatClient) isClosed() bool {return me.closeFlag != 0}

func (me *tChatClient) isNotClosed() bool {return !me.isClosed()
}

func (me *tChatClient) Send(msg IMsg) {if me.isNotClosed() {me.sendChan <- msg}
}

func (me *tChatClient) RecvHandler(handler RecvFunc) {if me.isNotClosed() {me.recvHandler = handler}
}


func (me *tChatClient) Close() {if me.isNotClosed() {me.closeConn()
    }
}

func (me *tChatClient) beginWrite() {writer := bufio.NewWriter(me.conn)
    newline := '\n'
    for {
        select {
        case <- me.closeChan:
            _ = me.conn.Close()
            me.closeFlag = 2
            return

        case msg := <- me.sendChan:
            _,e := writer.WriteString(msg.Encode())
            if e != nil {me.closeConn()

            } else {_,e = writer.WriteRune(newline)
                if e != nil {me.closeConn()
                }
            }
        }
    }
}

func (me *tChatClient) beginRead() {reader := bufio.NewReader(me.conn)
    for me.isNotClosed() {line, _, err := reader.ReadLine()
        if err != nil {break}

        ok, msg := MsgBuilder.Build(string(line))
        if ok {
            fn := me.recvHandler
            if fn != nil {fn(msg)
            }
        }
    }
}

func (me *tChatClient) closeConn() {if atomic.CompareAndSwapInt32(&me.closeFlag, 0, 1) {me.closeChan <- true}
}

(未完待续)

退出移动版