Sembang satu lawan satu dengan soket golang

王林
Lepaskan: 2024-02-08 20:45:04
ke hadapan
363 orang telah melayarinya

与 golang 套接字一对一聊天

Kandungan soalan

Saya mempunyai apl beli-belah di mana pengguna boleh menyiarkan ketersediaan dan pengguna lain boleh mencarinya dan menambah ketersediaan mereka.

Saya kini mempunyai perkhidmatan sembang, pada asasnya untuk berbual. Iaitu, pelanggan boleh berbual dengan pembeli untuk mengesahkan butiran atau perkara lain. Sembang ini sepatutnya satu-satu. Jadi mungkin terdapat 5 pelanggan yang bertanya tentang siaran beli-belah dan saya mahu sembang itu unik kerana sembang pelanggan A tentang pembelian harus berasingan daripada sembang pelanggan B tentang pembelian yang sama. Pembeli sepatutnya dapat melihat sembang dan membalas.

Inilah yang saya miliki sekarang, tetapi ini nampaknya menyiarkan mesej kepada semua orang dalam rujukan. Saya hanya mahu pembeli-belah menerima mesej daripada pengirim tertentu tanpa orang lain mempunyai akses kepada sembang.

"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
    }
}
Salin selepas log masuk

"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)
                    }
                }
            }
        }
    }
}
Salin selepas log masuk

"pengendali.pergi"

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)
}
Salin selepas log masuk

"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)
Salin selepas log masuk
"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"`
    }
Salin selepas log masuk


Jawapan betul


Sebab utama kod anda menyiarkan mesej kepada semua orang di dalam bilik dengan rujukan yang sama ialah anda melakukannya dalam kaedah 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
            ...
Salin selepas log masuk

Saya harap skor 1-1. Sebut harga adalah postid

Jika referencepostid , dan anda mahu komunikasi satu dengan satu antara pembeli (orang yang menghantar siaran ketersediaan) dan setiap pelanggan, maka anda perlu memastikan setiap sembang boleh dikenal pasti secara unik.

Kunci unik untuk setiap sesi sembang mestilah gabungan postid (reference) 和客户 id (sender). Ini memastikan setiap pelanggan mempunyai sesi sembang unik dengan pembeli dalam setiap siaran.

Anda kemudian boleh mengemas kini gabungan client 结构,使其具有 chatid ,它是 referencesender.

type client struct {
    ...
    chatid string `json:"chat_id"`
}
Salin selepas log masuk

Anda boleh mengemas kini logo hub 来管理聊天会话地图(由 chatid) dan bukannya bilik.

type hub struct {
    chats      map[string]*chat
    ...
}
Salin selepas log masuk

Struktur setiap chat adalah seperti berikut:

type chat struct {
    shopper *client
    customer *client
}
Salin selepas log masuk

Untuk mengendalikan mesej, apabila pelanggan menghantar mesej, mesej itu dihantar kepada pembeli, dan apabila pembeli membalas, mesej itu dihantar kepada pelanggan. Penghalaan boleh dilakukan menggunakan chatid.

Sebagai contoh, dalam logik broadcast anda:

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
        }
    }
Salin selepas log masuk
Pembolehubah

m ialah jenis *message dan ia tidak mempunyai medan m 变量的类型为 *message,它没有 chatid.
Untuk menyelesaikan masalah ini, anda harus mempertimbangkan untuk menambah medan chatid 字段添加到 message pada struktur

.

messagePertama, kami mengubah suai struktur

:

type message struct {
    content string `json:"content"`
    chatid  string `json:"chat_id"`
    sender  string `json:"sender"`
}
Salin selepas log masuk
clientreadmessage 方法中构造一个新的 messageKemudian, apabila anda membina

baharu dalam kaedah baca mesej client:

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

Apabila memulakan sembang:

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
    }
}
Salin selepas log masuk
Amaran: Ini hanyalah rangka asas ciri, tetapi ia tidak mengambil kira kerumitan tambahan, seperti mengendalikan situasi di mana pelanggan atau pembeli mungkin mempunyai berbilang peranti, memastikan pengendalian ralat yang mantap dan banyak lagi. 🎜

Atas ialah kandungan terperinci Sembang satu lawan satu dengan soket golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:stackoverflow.com
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan
Tentang kita Penafian Sitemap
Laman web PHP Cina:Latihan PHP dalam talian kebajikan awam,Bantu pelajar PHP berkembang dengan cepat!