美文网首页
Go 通过 TCP 实现登录通信聊天

Go 通过 TCP 实现登录通信聊天

作者: 三梦哥 | 来源:发表于2022-12-23 22:27 被阅读0次

    Server 端

    go run main.go
    
    package main
    
    import (
        "encoding/json"
        "log"
        "net"
        "sync"
    )
    
    type User struct {
        Uid  string
        Name string
        conn *ConnInfo
    }
    
    type ConnInfo struct {
        conn net.Conn
        user *User
    }
    
    type SessionManager struct {
        sync.RWMutex
        users map[string]*User
    }
    
    func NewSessionManager() *SessionManager {
        return &SessionManager{
            users: make(map[string]*User),
        }
    }
    
    func (s *SessionManager) User(uid string) (*User, bool) {
        s.RLock()
        defer s.RUnlock()
        u, ok := s.users[uid]
        return u, ok
    }
    
    func (s *SessionManager) AddUser(u *User) {
        s.Lock()
        s.users[u.Uid] = u
        s.Unlock()
    }
    
    func (s *SessionManager) RemoveUser(uid string) {
        s.Lock()
        delete(s.users, uid)
        s.Unlock()
    }
    
    var manager = NewSessionManager()
    
    func Start() {
        listener, err := net.Listen("tcp", ":8099")
        if err != nil {
            panic(err)
        }
        for {
            conn, err := listener.Accept()
            if err != nil {
                break
            }
            log.Printf("remote conn:%s", conn.RemoteAddr())
            go handleConn(conn)
        }
    }
    
    type LoginProto struct {
        Uid  string `json:"uid"`
        Name string `json:"name"`
    }
    
    type MessageDataProto struct {
        To      string `json:"to"`
        Message string `json:"message"`
    }
    
    type CommandProto struct {
        Command string `json:"command"`
        Content string `json:"content"`
    }
    
    func handleConn(conn net.Conn) {
        connInfo := &ConnInfo{
            conn: conn,
        }
        buf := make([]byte, 1024)
        for {
            n, err := conn.Read(buf)
            if err != nil {
                log.Printf("conn %s read err:%s", conn.RemoteAddr(), err.Error())
                if connInfo.user != nil {
                    manager.RemoveUser(connInfo.user.Uid)
                    log.Printf("uid %s is offline", connInfo.user.Uid)
                }
                break
            }
            var cmd CommandProto
            if err := json.Unmarshal(buf[:n], &cmd); err != nil {
                log.Printf("failed to unmarshal buf data:%s", buf[:n])
                continue
            }
            switch cmd.Command {
            case "Login":
                var login LoginProto
                if err := json.Unmarshal([]byte(cmd.Content), &login); err != nil {
                    log.Printf("failed to unmarshal content data:%s", cmd.Content)
                    continue
                }
                u := &User{
                    Uid:  login.Uid,
                    Name: login.Name,
                    conn: connInfo,
                }
                connInfo.user = u
                manager.AddUser(u)
                log.Printf("uid %s,name:%s login successfully", u.Uid, u.Name)
            case "Send":
                if connInfo.user == nil {
                    log.Println("user doesn't login")
                    continue
                }
                var msg MessageDataProto
                if err := json.Unmarshal([]byte(cmd.Content), &msg); err != nil {
                    log.Printf("failed to unmarshal content data:%s", cmd.Content)
                    continue
                }
                toUser, exist := manager.User(msg.To)
                if !exist {
                    log.Printf("peer user %s is offline", msg.To)
                    continue
                }
                data := struct {
                    From    string
                    Message string
                }{
                    From:    connInfo.user.Uid,
                    Message: msg.Message,
                }
                if err := json.NewEncoder(toUser.conn.conn).Encode(data); err != nil {
                    log.Printf("send data from %s to %s err:%s", connInfo.user.Uid, msg.To, err.Error())
                    continue
                }
            default:
                log.Printf("unknown command:%s", cmd.Command)
            }
        }
    }
    
    func main() {
        log.SetFlags(log.LstdFlags | log.Lshortfile)
        Start()
    }
    

    client 端

    通过telnet连接Server

    telnet localhost 8099
    

    然后再发送登录uid:001指令

    {
        "command":"Login",
        "content":" { \"uid\":\"001\", \"name\":\"Tom\" } "
    }
    

    再起一个窗口,也通过telnet连接Server

    telnet localhost 8099
    

    然后再发送登录uid:002指令

    {
        "command":"Login",
        "content":" { \"uid\":\"002\", \"name\":\"Jack\" } "
    }
    

    然后在之前的uid:001窗口给对端uid:002发送消息指令

    {
        "command":"Send",
        "content":" { \"to\":\"002\", \"message\":\"Hello Jack\" } "
    }
    
    image.png
    可以看到,uid:002 收到了uid:001 发过来的Hello Jack消息了。

    相关文章

      网友评论

          本文标题:Go 通过 TCP 实现登录通信聊天

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