Heim  >  Artikel  >  Backend-Entwicklung  >  Chatten Sie eins zu eins mit Golang Socket

Chatten Sie eins zu eins mit Golang Socket

王林
王林nach vorne
2024-02-08 20:45:04348Durchsuche

与 golang 套接字一对一聊天

Frageninhalt

Ich habe eine Shopping-App, in der Benutzer Verfügbarkeiten posten können und andere Benutzer sie finden und ihre Verfügbarkeit hinzufügen können.

Ich habe jetzt einen Chat-Dienst, hauptsächlich zum Chatten. Das heißt, Kunden können mit dem Käufer chatten, um Details oder andere Dinge zu bestätigen. Dieser Chat sollte ein Einzelgespräch sein. Es können also fünf Kunden sein, die nach einem Einkaufsbeitrag fragen, und ich möchte, dass der Chat einzigartig ist, da der Chat von Kunde A über den Kauf vom Chat von Kunde B über denselben Kauf getrennt sein sollte. Käufer sollten den Chat sehen und antworten können.

Das ist es, was ich derzeit habe, aber dies scheint eine Botschaft an alle in der Referenz zu senden. Ich möchte, dass Käufer nur Nachrichten von einem bestimmten Absender erhalten, ohne dass andere Zugriff auf den Chat haben.

"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)
                    }
                }
            }
        }
    }
}

"handler.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)
}

"route.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"`
    }

Richtige Antwort


Der Hauptgrund, warum Ihr Code eine Nachricht mit derselben Referenz an alle im Raum sendet, ist, dass Sie es in der hub broadcast 频道中处理消息的方式。在当前的实现中,当发送消息时,它会被转发到同一房间中的每个客户端(即具有相同的引用)。这是在 hubrun-Methode tun:

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

Ich hoffe, dass es 1:1 steht. Das Zitat ist gepostet

Wenn referencepostid Sie eine persönliche Kommunikation zwischen dem Käufer (der Person, die den Verfügbarkeitsbeitrag veröffentlicht) und jedem Kunden wünschen, müssen Sie sicherstellen, dass jeder Chat eindeutig identifizierbar ist. p>

Der eindeutige Schlüssel für jede Chat-Sitzung sollte eine Kombination aus postid (reference) 和客户 id (sender sein. Dadurch wird sichergestellt, dass jeder Kunde in jedem Beitrag eine einzigartige Chat-Sitzung mit dem Käufer hat.

Sie können dann die Kombination von client 结构,使其具有 chatid ,它是 referencesender aktualisieren.

type client struct {
    ...
    chatid string `json:"chat_id"`
}

Sie können das hub 来管理聊天会话地图(由 chatid-Logo anstelle des Raums aktualisieren.

type hub struct {
    chats      map[string]*chat
    ...
}

Die Struktur jedes chat ist wie folgt:

type chat struct {
    shopper *client
    customer *client
}

Um Nachrichten zu verarbeiten: Wenn ein Kunde eine Nachricht sendet, wird die Nachricht an den Käufer gesendet, und wenn der Käufer antwortet, wird die Nachricht an den Kunden gesendet. Das Routing kann mit chatid erfolgen.

Zum Beispiel in deiner broadcast Logik:

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
        }
    }
Die Variable

m ist vom Typ *message und hat kein m 变量的类型为 *message,它没有 chatid-Feld.
Um dieses Problem zu lösen, sollten Sie erwägen, das chatid 字段添加到 message-Feld zur

-Struktur hinzuzufügen.

messageZuerst modifizieren wir die

Struktur:

type message struct {
    content string `json:"content"`
    chatid  string `json:"chat_id"`
    sender  string `json:"sender"`
}
clientreadmessage 方法中构造一个新的 messageDann, wenn Sie ein neues

in der readmessage-Methode des Clients erstellen:

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

Beim Initialisieren des Chats:

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
    }
}

Warnung: Dies ist nur ein Grundgerüst der Funktion, berücksichtigt jedoch keine zusätzlichen Komplexitäten, wie z. B. die Handhabung von Situationen, in denen Kunden oder Käufer möglicherweise mehrere Geräte haben, die Gewährleistung einer robusten Fehlerbehandlung und mehr. 🎜

Das obige ist der detaillierte Inhalt vonChatten Sie eins zu eins mit Golang Socket. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:stackoverflow.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen