Maison  >  Article  >  développement back-end  >  Discutez en tête-à-tête avec la prise Golang

Discutez en tête-à-tête avec la prise Golang

王林
王林avant
2024-02-08 20:45:04380parcourir

与 golang 套接字一对一聊天

Contenu de la question

J'ai une application d'achat où les utilisateurs peuvent publier leur disponibilité et d'autres utilisateurs peuvent les trouver et ajouter leur disponibilité.

Je dispose désormais d'un service de chat, essentiellement pour discuter. Autrement dit, les clients peuvent discuter avec l'acheteur pour confirmer des détails ou d'autres choses. Cette discussion devrait être en tête-à-tête. Il peut donc y avoir 5 clients qui posent des questions sur une publication d'achat et je souhaite que la discussion soit unique, car la discussion du client A sur les achats doit être distincte de la discussion du client B sur les mêmes achats. Les acheteurs devraient pouvoir voir le chat et répondre.

C'est ce que j'ai actuellement, mais cela semble diffuser un message à tout le monde dans la référence. Je souhaite uniquement que les acheteurs reçoivent des messages d'un expéditeur spécifique sans que d'autres aient accès au chat.

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

Bonne réponse


La principale raison pour laquelle votre code diffuse un message à toutes les personnes présentes dans la pièce avec la même référence est que vous le faites selon la méthode 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
            ...

J'espère que le score est de 1-1. La citation est postid

Si referencepostid et que vous souhaitez une communication individuelle entre l'acheteur (la personne publiant la publication de disponibilité) et chaque client, vous devez vous assurer que chaque discussion est identifiable de manière unique. p>

La clé unique pour chaque session de chat doit être une combinaison de postid (reference) 和客户 id (sender). Cela garantit que chaque client dispose d'une session de discussion unique avec l'acheteur dans chaque publication.

Vous pouvez ensuite mettre à jour la combinaison de client 结构,使其具有 chatid ,它是 referencesender.

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

Vous pouvez mettre à jour le logo hub 来管理聊天会话地图(由 chatid) à la place de la pièce.

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

La structure de chacun chat est la suivante :

type chat struct {
    shopper *client
    customer *client
}

Pour gérer les messages, lorsqu'un client envoie un message, le message est envoyé à l'acheteur, et lorsque l'acheteur répond, le message est envoyé au client. Le routage peut être effectué en utilisant chatid.

Par exemple, dans votre broadcast logique :

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

m est de type *message et elle n'a pas de champ m 变量的类型为 *message,它没有 chatid.
Pour résoudre ce problème, vous devriez envisager d'ajouter le champ chatid 字段添加到 message à la structure

.

messageTout d'abord, nous modifions la

structure :

type message struct {
    content string `json:"content"`
    chatid  string `json:"chat_id"`
    sender  string `json:"sender"`
}
clientreadmessage 方法中构造一个新的 messageEnsuite, lorsque vous construisez un nouveau

dans la méthode readmessage du client :

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

Lors de l'initialisation du chat :

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

Avertissement : il ne s'agit que d'un squelette de base de la fonctionnalité, mais il ne prend pas en compte des complexités supplémentaires, telles que la gestion des situations dans lesquelles les clients ou les acheteurs peuvent disposer de plusieurs appareils, la garantie d'une gestion robuste des erreurs, et bien plus encore. 🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer