英文:
Gorilla websocket error: close 1007 Illegal UTF-8 Sequence
问题
我正在尝试为GlassFish实现一个WebSocket代理服务器。如果我尝试连接多个客户端,我会遇到以下错误:
ReadMessage Failed: websocket: close 1007 Illegal UTF-8 Sequence.
我确定GlassFish服务器发送的数据是正确的,因为与使用node.js实现的另一个代理服务器相比,相同的服务器可以正常工作。
func GlassFishHandler(conn *websocket.Conn){
defer conn.Close()
conn.SetReadDeadline(time.Now().Add(1000 * time.Second))
conn.SetWriteDeadline(time.Now().Add(1000 * time.Second))
fmt.Println("WS-GOLANG PROXY SERVER: Connected to GlassFish")
for {
messageType, reader, err := conn.NextReader()
if err != nil {
fmt.Println("ReadMessage Failed: ", err) // <- error here
} else {
message, err := ioutil.ReadAll(reader)
if (err == nil && messageType == websocket.TextMessage){
var dat map[string]interface{}
if err := json.Unmarshal(message, &dat); err != nil {
panic(err)
}
// get client destination id
clientId := dat["target"].(string)
fmt.Println("Msg from GlassFish for Client: ", dat);
// pass through
clients[clientId].WriteMessage(websocket.TextMessage, message)
}
}
}
}
以上是你要翻译的内容。
英文:
I'm trying to implement a websocket proxy server for GlassFish. If I try to connect more than one client I'm getting error:
> ReadMessage Failed: websocket: close 1007 Illegal UTF-8 Sequence.
I'm sure the GlassFish server sending right data, because the same server works properly with another proxy server implemented with node.js.
func GlassFishHandler(conn *websocket.Conn){
defer conn.Close()
conn.SetReadDeadline(time.Now().Add(1000 * time.Second))
conn.SetWriteDeadline(time.Now().Add(1000 * time.Second))
fmt.Println("WS-GOLANG PROXY SERVER: Connected to GlassFish")
for {
messageType, reader, err := conn.NextReader()
if err != nil {
fmt.Println("ReadMessage Failed: ", err) // <- error here
} else {
message, err := ioutil.ReadAll(reader)
if (err == nil && messageType == websocket.TextMessage){
var dat map[string]interface{}
if err := json.Unmarshal(message, &dat); err != nil {
panic(err)
}
// get client destination id
clientId := dat["target"].(string)
fmt.Println("Msg from GlassFish for Client: ", dat);
// pass through
clients[clientId].WriteMessage(websocket.TextMessage, message)
}
}
}
}
答案1
得分: 3
总结我的评论作为答案:
当你写给客户端时,你从GlassFish消息中获取clientId,从映射中获取客户端,然后写入它 - 基本上是clients[clientId].WriteMessage(...)
。
虽然你的映射访问可能是线程安全的,但写入不是,因为可以看作是:
// 映射访问 - 如果你使用并发映射可能是安全的
client := clients[clientId]
// 写入客户端,没有任何保护
client.WriteMessage(...)
所以可能发生的情况是,两个独立的goroutine同时向同一个客户端写入。你应该通过在WriteMessage
方法实现中添加互斥锁来保护你的客户端。
顺便说一句,实际上,与其用互斥锁保护这个方法,更好、更符合Go语言风格的方法是使用通道来写入消息,并为每个客户端启动一个goroutine来从通道中消费并写入实际的套接字。
所以在client结构体中,我会这样做:
type message struct {
msgtype string
msg string
}
type client struct {
...
msgqueue chan *message
}
func (c *client)WriteMessage(messageType, messageText string) {
// 这里我简化了,但你明白我的意思
c.msgqueue <- &message{msgtype: messageType, msg: messageText}
}
func (c *client)writeLoop() {
go func() {
for msg := range c.msgqueue {
c.actuallyWriteMessage(msg)
}
}()
}
当创建一个新的客户端实例时,只需启动写循环。
英文:
Summing up my comments as an answer:
When you are writing to the client, you are taking the clientId from the GlassFish message, fetching the client from a map, and then writing to it - basically clients[clientId].WriteMessage(...)
.
While your map access can be thread safe, writing is not, as this can be seen as:
// map access - can be safe if you're using a concurrent map
client := clients[clientId]
// writing to a client, not protected at all
client.WriteMessage(...)
So what's probably happening is that two separate goroutines are writing to the same client at the same time. You should protect your client from it by adding a mutex in the WriteMessage
method implementation.
BTW actually instead of protecting this method with a mutex, a better, more "go-ish" approach would be to use a channel to write the message, and a goroutine per client that consumes from the channel and writes to the actual socket.
So in the client struct I'd do something like this:
type message struct {
msgtype string
msg string
}
type client struct {
...
msgqueue chan *message
}
func (c *client)WriteMessage(messageType, messageText string) {
// I'm simplifying here, but you get the idea
c.msgqueue <- &message{msgtype: messageType, msg: messageText}
}
func (c *client)writeLoop() {
go func() {
for msg := ragne c.msgqueue {
c.actuallyWriteMessage(msg)
}
}()
}
and when creating a new client instance, just launch the write loop
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论