美文网首页设计模式
手撸golang GO与微服务 ChatServer之1

手撸golang GO与微服务 ChatServer之1

作者: 老罗话编程 | 来源:发表于2021-03-07 16:44 被阅读0次

    缘起

    最近阅读<<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
        }
    }
    

    (未完待续)

    相关文章

      网友评论

        本文标题:手撸golang GO与微服务 ChatServer之1

        本文链接:https://www.haomeiwen.com/subject/ptjyqltx.html