缘起

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

案例需要(聊天服务器)

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

指标

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

设计

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

IMsg.go

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

package chat_serverimport (    "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_serverimport (    "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_servertype IChatClient interface {    Dial(address string) error    Send(msg IMsg)    RecvHandler(handler RecvFunc)    Close()}type RecvFunc func(msg IMsg)

tChatClient.go

聊天客户端, 实现IChatClient接口

package chat_serverimport (    "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    }}

(未完待续)