Chat one on one with golang socket

王林
Release: 2024-02-08 20:45:04
forward
317 people have browsed it

与 golang 套接字一对一聊天

Question content

I have a shopping app where users can post availability and other users can find them and add their availability.

I now have a chat service that is essentially for chatting. That is, customers can chat with the shopper to confirm details or other things. This chat should be one-on-one. So there might be 5 customers asking about a shopping post and I want the chat to be unique because customer A's chat about shopping should be separate from customer B's chat about the same shopping. Shoppers should be able to see the chat and respond.

This is what I currently have, but this seems to be broadcasting a message to everyone in the reference. I only want shoppers to receive messages from a specific sender without others having access to the 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 } }
Copy after login

“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) } } } } } }
Copy after login

"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) }
Copy after login

"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)
Copy after login
"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"` }
Copy after login


Correct answer


The main reason your code broadcasts a message to everyone in the room with the same reference is that you are inhub##broadcastThe way messages are processed in the channel. In the current implementation, when a message is sent, it is forwarded to every client in the same room (i.e. with the same reference). This is done in therunmethod ofhub:

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

I hope the score is 1-1. The reference is postid

If

referenceispostidand you want one-on-one communication between the shopper (the person who posted the availability post) and each customer, then you need to ensure that each Each chat is uniquely identifiable. The unique key for each chat session should be the combination of

postid(reference) and customer id (sender). This ensures each customer has a unique chat session with the shopper in every post.

You can then update the

clientstructure to have achatid, which is a combination ofreferenceandsender.

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

You can update the

hubto manage chat session maps (identified bychatid) instead of rooms.

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

The structure of each

chatis as follows:

type chat struct { shopper *client customer *client }
Copy after login

To handle messages, when a customer sends a message, the message is sent to the shopper, and when the shopper replies, the message is sent to the customer. Routing can be done using

chatid.

For example, in your

broadcastlogic:

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 } }
Copy after login

mThe variable is of type*messageand it has nochatidfield.To resolve this issue, you should consider adding the
chatidfield to themessagestructure.

First, we modify the

messagestructure:

type message struct { content string `json:"content"` chatid string `json:"chat_id"` sender string `json:"sender"` }
Copy after login

Then, when you construct a new

messagein thereadmessagemethod ofclient:

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

When initializing 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 } }
Copy after login
Warning: This is just a basic skeleton of the functionality, but it does not take into account additional complexities, such as handling situations where customers or shoppers may have multiple devices, ensuring robust error handling, and more.

The above is the detailed content of Chat one on one with golang socket. For more information, please follow other related articles on the PHP Chinese website!

source:stackoverflow.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!