【发布时间】:2018-06-20 18:24:02
【问题描述】:
我正在尝试使用 gorilla/websocket 包实现 websocket。我按照这个例子,它工作:https://github.com/gorilla/websocket/tree/master/examples/chat 但是,我正在尝试根据 user_id 向特定用户发送消息。
JS(vue.js)代码(简体):
data() {
return {
user_id: 2,
username: "test",
userWebSocket: ""
}
},
connectUserToWebSocket() {
if (window.WebSocket) {
var loc = window.location;
var uri = 'ws:';
if (loc.protocol === 'https:') {
uri = 'wss:';
}
uri += '//localhost:8000/api/ws';
this.userWebSocket = new WebSocket(uri)
this.userWebSocket.onopen = function() {
console.log('Connected')
}
this.userWebSocket.onmessage = function(evt) {
alert(evt.data)
}
}
}
还有 main.go(简化版):
import (
"github.com/gorilla/mux"
"github.com/urfave/negroni"
"test/notifications"
)
func Router() http.Handler {
r := mux.NewRouter()
hub := notifications.NewHub()
go hub.Run()
r.HandleFunc("/", controller.Root).Methods("GET")
//r.HandleFunc("/api/ws", notifications.HandleConnections)
r.HandleFunc("/api/ws", func(w http.ResponseWriter, r *http.Request) {
notifications.ServeWs(hub, w, r)
})
还有 notification.go: 包裹通知
import (
"net/http"
"log"
"github.com/gorilla/websocket"
"time"
"bytes"
"fmt"
)
const (
// Time allowed to write a message to the peer.
writeWait = 10 * time.Second
// Time allowed to read the next pong message from the peer.
pongWait = 60 * time.Second
// Send pings to peer with this period. Must be less than pongWait.
pingPeriod = (pongWait * 9) / 10
// Maximum message size allowed from peer.
maxMessageSize = 512
)
var (
newline = []byte{'\n'}
space = []byte{' '}
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
CheckOrigin: func(r *http.Request) bool {
return true
},
}
type Client struct {
hub *Hub
Id int
// The websocket connection.
conn *websocket.Conn
// Buffered channel of outbound messages.
send chan []byte
}
type Hub struct {
// Registered clients.
clients map[*Client]bool
users map[int]*Client
// Inbound messages from the clients.
broadcast chan []byte
// Register requests from the clients.
register chan *Client
// Unregister requests from clients.
unregister chan *Client
}
func NewHub() *Hub {
return &Hub{
broadcast: make(chan []byte),
register: make(chan *Client),
unregister: make(chan *Client),
clients: make(map[*Client]bool),
users: make(map[int]*Client),
}
}
func (h *Hub) Run() {
for {
select {
case client := <-h.register:
h.clients[client] = true
case client := <-h.unregister:
if _, ok := h.clients[client]; ok {
delete(h.clients, client)
close(client.send)
}
case message := <-h.broadcast:
for client := range h.clients {
select {
case client.send <- message:
default:
close(client.send)
delete(h.clients, client)
}
}
}
}
}
// readPump pumps messages from the websocket connection to the hub.
//
// The application runs readPump in a per-connection goroutine. The application
// ensures that there is at most one reader on a connection by executing all
// reads from this goroutine.
func (c *Client) readPump() {
defer func() {
c.hub.unregister <- c
c.conn.Close()
}()
c.conn.SetReadLimit(maxMessageSize)
c.conn.SetReadDeadline(time.Now().Add(pongWait))
c.conn.SetPongHandler(func(string) error { c.conn.SetReadDeadline(time.Now().Add(pongWait)); return nil })
for {
_, message, err := c.conn.ReadMessage()
if err != nil {
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {
log.Printf("error: %v", err)
}
break
}
message = bytes.TrimSpace(bytes.Replace(message, newline, space, -1))
c.hub.broadcast <- message
}
}
// writePump pumps messages from the hub to the websocket connection.
//
// A goroutine running writePump is started for each connection. The
// application ensures that there is at most one writer to a connection by
// executing all writes from this goroutine.
func (c *Client) writePump() {
ticker := time.NewTicker(pingPeriod)
defer func() {
ticker.Stop()
c.conn.Close()
}()
for {
select {
case message, ok := <-c.send:
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
if !ok {
// The hub closed the channel.
c.conn.WriteMessage(websocket.CloseMessage, []byte{})
return
}
w, err := c.conn.NextWriter(websocket.TextMessage)
if err != nil {
return
}
w.Write(message)
// Add queued chat messages to the current websocket message.
n := len(c.send)
for i := 0; i < n; i++ {
w.Write(newline)
w.Write(<-c.send)
}
if err := w.Close(); err != nil {
return
}
case <-ticker.C:
c.conn.SetWriteDeadline(time.Now().Add(writeWait))
if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
return
}
}
}
}
// serveWs handles websocket requests from the peer.
func ServeWs(hub *Hub, w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
fmt.Println(err)
log.Println(err)
return
}
client := &Client{hub: hub, conn: conn, send: make(chan []byte, 256)}
client.hub.register <- client
// Allow collection of memory referenced by the caller by doing all work in
// new goroutines.
go client.writePump()
go client.readPump()
}
问题是我不知道如何将 JS 中的 user_id 添加到连接中。我也想保持它的安全。
如您所见,我尝试了以下方法:
type Client struct {
...
Id int
}
还有:
type Hub struct {
// Registered clients.
clients map[*Client]bool
users map[int]*Client
...
}
还有:
func NewHub() *Hub {
return &Hub{
...
clients: make(map[*Client]bool),
users: make(map[int]*Client),
}
}
但是我还是不知道怎么用user_id打开websocket。
TL:DR 1. 我有工作代码向所有用户发送通知。 2. 我想根据 ID 向特定用户发送通知。
根本没有帮助的类似问题: How to send to only one client and not all clients using Go and gorilla websocket
【问题讨论】:
-
您可以向客户端(浏览器/js)发送一条指令,要求它“初始化对话”,即提供用户名/uid。然后客户端发回所需的数据。您将需要创建数据结构,似乎 json 可以很好地完成这项工作。
-
将用户 ID 与消息一起传递到集线器。修改集线器以仅向该用户发送带有用户 ID 的消息。见stackoverflow.com/questions/46415206/golang-one-to-one-chat
-
我实际上不会收到用户发送的任何消息。我想要的只是通知系统,所以只有服务器会返回一些数据给用户。
-
要发送给特定用户,请在
users中查找用户并仅发送给该用户。我可能不明白这个问题,因为这似乎有些明显。 -
但是要在用户中查找,首先我应该在打开 websocket 时将用户添加到具有特定 ID 的用户。正如您所读到的,这就是问题所在。
标签: javascript go