首頁 > 後端開發 > Golang > 主體

與 golang 套接字一對一聊天

王林
發布: 2024-02-08 20:45:04
轉載
364 人瀏覽過

与 golang 套接字一对一聊天

問題內容

我有一個購物應用程序,用戶可以在其中發布可用性,其他用戶可以找到他們並加入他們的可用性。

我現在有一個聊天服務,本質上是為了聊天。即顧客可以與購物者聊天以確認細節或其他事情。這次聊天應該是一對一的。因此,可能有 5 個客戶詢問購物帖子,我希望聊天是唯一的,因為客戶 a 關於購物的聊天應該與客戶 b 關於同一購物的聊天分開。購物者應該能夠看到聊天並做出回應。

這是我目前所擁有的,但這似乎是向參考中的每個人廣播訊息。我只想讓購物者收到特定寄件者發送的訊息,而其他人無法存取聊天。

“client.go”

type client struct {
    conn           *websocket.conn
    chatrepository chat.chatrepository
    message        chan *message
    id             string `json:"id"`
    reference      string `json:"reference"`
    username       string `json:"username"`
    sender         string `json:"sender"`
    recipient      string `json:"recipient"`
}

type message struct {
    content   string `json:"content"`
    reference string `json:"reference"`
    // username  string `json:"username"`
    sender string `json:"sender"`
}

func (c *client) writemessage() {
    defer func() {
        c.conn.close()
    }()

    for {
        message, ok := <-c.message
        if !ok {
            return
        }
        uuid, err := uuid.newv4()
        if err != nil {
            log.fatalf("failed to generate uuid: %v", err)
        }
        chatmessage := chat.chatmessage{
            id:     uuid.string(),
            sender: message.sender,
            // recipient: recipient,
            timestamp: time.now(),
            content:   message.content,
        }
        if c.sender == message.sender {
            _, errx := c.chatrepository.addmessage(message.reference, chatmessage)
            if err != nil {
                log.fatalf("failed to generate uuid: %v", errx)
            }
        }
        c.conn.writejson(chatmessage)
    }
}

func (c *client) readmessage(hub *hub) {
    defer func() {
        hub.unregister <- c
        c.conn.close()
    }()

    for {
        _, m, err := c.conn.readmessage()
        if err != nil {
            if websocket.isunexpectedcloseerror(err, websocket.closegoingaway, websocket.closeabnormalclosure) {
                log.printf("error: %v", err)
            }
            break
        }

        msg := &message{
            content:   string(m),
            reference: c.reference,
            sender:    c.sender,
            // username:  c.username,
        }

        hub.broadcast <- msg
    }
}
登入後複製

“hub.go”

type room struct {
    id      string             `json:"id"`
    name    string             `json:"name"`
    clients map[string]*client `json:"clients"`
}

type hub struct {
    rooms      map[string]*room
    register   chan *client
    unregister chan *client
    broadcast  chan *message
    emmiter    events.emitter
}

func newhub(emmiter events.emitter) *hub {
    return &hub{
        rooms:      make(map[string]*room),
        register:   make(chan *client),
        unregister: make(chan *client),
        broadcast:  make(chan *message, 5),
        emmiter:    emmiter,
    }
}

func (h *hub) run() {
    for {
        select {
        case cl := <-h.register:
            if _, ok := h.rooms[cl.reference]; ok {
                r := h.rooms[cl.reference]

                if _, ok := r.clients[cl.id]; !ok {
                    r.clients[cl.id] = cl
                }

            }
        case cl := <-h.unregister:
            if _, ok := h.rooms[cl.reference]; ok {
                if _, ok := h.rooms[cl.reference].clients[cl.id]; ok {
                    // if len(h.rooms[cl.reference].clients) != 0 {
                    //  h.broadcast <- &message{
                    //      content:   "user left the chat",
                    //      reference: cl.reference,
                    //      username:  cl.username,
                    //  }
                    // }

                    delete(h.rooms[cl.reference].clients, cl.id)
                    close(cl.message)
                }
            }

        case m := <-h.broadcast:
            if _, ok := h.rooms[m.reference]; ok {
                for _, cl := range h.rooms[m.reference].clients {
                    cl.message <- m
                    if m.sender != cl.recipient {
                        notifications.sendpush(h.emmiter, cl.recipient, fmt.sprintf("new message from %v", cl.username), m.content)
                    }
                }
            }
        }
    }
}
登入後複製

“處理程序.go”

type handler struct {
    hub            *hub
    chatrepository chat.chatrepository
}

func newhandler(h *hub, chatrepository chat.chatrepository) *handler {
    return &handler{
        hub:            h,
        chatrepository: chatrepository,
    }
}

var upgrader = websocket.upgrader{
    readbuffersize:  1024,
    writebuffersize: 1024,
    checkorigin: func(r *http.request) bool {
        return true
    },
}

func (h *handler) joinroom(c *gin.context) {
    conn, err := upgrader.upgrade(c.writer, c.request, nil)
    if err != nil {
        utils.handleerror(c, nil, "error creating chat connection", http.statusbadgateway)
        return
    }

    reference := c.param("reference")
    sender := c.query("sender")
    username := c.query("username")
    recipient := c.query("recipient")

    if reference == "" || sender == "" || username == "" || recipient == "" {
        utils.handleerror(c, nil, "required parameters missing", http.statusbadgateway)
        return
    }
    if _, ok := h.hub.rooms[reference]; !ok {
        // room doesn't exist, handle accordingly
        _, err1 := h.chatrepository.getchathistory(reference)
        if err1 != nil {
            log.printf("failed to retrieve chat history: %s", err1)
            errx := h.chatrepository.createchat(reference)
            if errx != nil {
                utils.handleerror(c, nil, "error storing connection", http.statusbadgateway)
                return
            }
        }
        h.hub.rooms[reference] = &room{
            id:      reference,
            name:    sender,
            clients: make(map[string]*client),
        }
    }

    cl := &client{
        conn:           conn,
        chatrepository: h.chatrepository,
        message:        make(chan *message, 10),
        id:             sender,
        reference:      reference,
        sender:         sender,
        username:       username,
        recipient:      recipient,
    }

    h.hub.register <- cl
    go cl.writemessage()
    cl.readmessage(h.hub)
}
登入後複製

「路線.go」

hub := ws.newhub(events.neweventemitter(conn))
    wshandler := ws.newhandler(hub, pr.newchatrepository(db, client))
    go hub.run()
v1.get("/chat/ws/:reference", g.guard([]string{"user", "admin", "dispatcher"}, nil), wshandler.joinroom)
登入後複製
"chat.model.go"

    type Chat struct {
        ID        string        `json:"id,omitempty" bson:"_id,omitempty"`
        Reference string        `json:"reference" bson:"reference"`
        Messages  []ChatMessage `json:"messages" bson:"messages"`
    }
    type ChatMessage struct {
        ID        string    `json:"id,omitempty" bson:"_id,omitempty"`
        Sender    string    `json:"sender" bson:"sender,omitempty"`
        Timestamp time.Time `json:"timestamp" bson:"timestamp,omitempty"`
        Content   string    `json:"content" bson:"content,omitempty"`
    }
登入後複製


正確答案


您的程式碼向房間中具有相同引用的每個人廣播訊息的主要原因是您在hub broadcast 頻道中處理訊息的方式。在當前的實作中,當發送訊息時,它會被轉發到同一房間中的每個客戶端(即具有相同的引用)。這是在 hubrun 方法中完成的:

case m := <-h.broadcast:
    if _, ok := h.rooms[m.reference]; ok {
        for _, cl := range h.rooms[m.reference].clients {
            cl.message <- m
            ...
登入後複製

我希望比數是1-1。引用是postid

如果referencepostid ,並且您希望購物者(發布可用性帖子的人)和每個客戶之間進行一對一的通信,那麼您需要確保每個聊天都是唯一可辨識的。

每個聊天會話的唯一金鑰應該是 postid (reference) 和客戶 id (sender) 的組合。這可確保每個客戶在每個貼文中都與購物者進行獨特的聊天會話。

然後,您可以更新 client 結構,使其具有 chatid ,它是 referencesender 的組合。

type client struct {
    ...
    chatid string `json:"chat_id"`
}
登入後複製

您可以更新 hub 來管理聊天會話地圖(由 chatid 標識)而不是房間。

type hub struct {
    chats      map[string]*chat
    ...
}
登入後複製

每個 chat 的結構如下:

type chat struct {
    shopper *client
    customer *client
}
登入後複製

為了處理訊息,當客戶發送訊息時,訊息會傳送給購物者,當購物者回覆時,訊息會傳送給客戶。可以使用 chatid 來完成路由。

例如,在您的 broadcast 邏輯中:

case m := <-h.broadcast:
    chat, ok := h.chats[m.chatid]
    if ok {
        if m.sender == chat.customer.id {
            // message is from customer to shopper
            chat.shopper.message <- m
        } else if m.sender == chat.shopper.id {
            // message is from shopper to customer
            chat.customer.message <- m
        }
    }
登入後複製

m 變數的型別為 *message,它沒有 chatid 欄位。
要解決此問題,您應該考慮將 chatid 欄位新增至 message 結構中。

首先,我們修改 message 結構體:

type message struct {
    content string `json:"content"`
    chatid  string `json:"chat_id"`
    sender  string `json:"sender"`
}
登入後複製

然後,當您在 clientreadmessage 方法中建構一個新的 message 時:

msg := &message{
    content:   string(m),
    chatid:    c.chatid,
    sender:    c.sender,
}
登入後複製

初始化聊天時:

chatID := reference + "_" + sender

cl := &Client{
    ...
    ChatID: chatID,
}

// If the chat session does not exist, create it.
if _, ok := h.hub.Chats[chatID]; !ok {
    h.hub.Chats[chatID] = &Chat{
        Customer: cl,
        Shopper: nil, // That will be set when the shopper joins.
    }
} else {
    // That assumes only the customer or the shopper can join, not both at the same time.
    if h.hub.Chats[chatID].Shopper == nil {
        h.hub.Chats[chatID].Shopper = cl
    } else {
        h.hub.Chats[chatID].Customer = cl
    }
}
登入後複製

警告:這只是該功能的基本框架,但它並未考慮額外的複雜性,例如處理客戶或購物者可能擁有多個設備的情況、確保強大的錯誤處理等等。

以上是與 golang 套接字一對一聊天的詳細內容。更多資訊請關注PHP中文網其他相關文章!

來源:stackoverflow.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!