Go 如何快速连续发送两条消息,同时接收所有消息

Go 如何快速连续发送两条消息,同时接收所有消息,go,websocket,gorilla,Go,Websocket,Gorilla,我有两个服务在单独的Docker容器中运行,它们使用Gorilla Websocket在彼此之间发送消息。我能够一次发送一条消息,但当我快速连续发送两条消息时,它们在一次读取期间到达接收方,导致我的解组失败 在发送方,我有一个循环,它发送两条消息: for _, result := range results { greetingMsg := Message{ TopicIdentifier: *bot.TopicIdentifier, UserIdent

我有两个服务在单独的Docker容器中运行,它们使用Gorilla Websocket在彼此之间发送消息。我能够一次发送一条消息,但当我快速连续发送两条消息时,它们在一次读取期间到达接收方,导致我的解组失败

在发送方,我有一个循环,它发送两条消息:

for _, result := range results {
    greetingMsg := Message{
        TopicIdentifier: *bot.TopicIdentifier,
        UserIdentifier:  botIdentifier,
        Message:         result,
    }

    msgBytes, err := json.Marshal(greetingMsg)
    if err != nil {
        log.Println("Sender failed marshalling greeting message with error " + err.Error())
    }

    log.Printf("Sender writing %d bytes of message\n%s\n", len(msgBytes), string(msgBytes))
    err = conn.WriteMessage(websocket.TextMessage, msgBytes)

    if err != nil {
        log.Printf("Sender failed to send message\n%s\nwith error %s ", string(msgBytes), err.Error())
    }
}
正如所料,在conn.WriteMessage()调用之前,我从中获得了两个日志:

在接收端,我正在收听以下内容:

_, msg, err := conn.ReadMessage()
fmt.Printf("Receiver received %d bytes of message %s\n", len(msg), string(msg))
日志消息会产生:

2019/12/12 06:23:29 Receiver received 282 bytes of message  {"topicIdentifier":"83892f58b4b0-4303-8973-4896eed67ce0","userIdentifier":"119ba709-77a3-4b34-92f0-2187ecab7fc5","message":"I am doing good"}
{"topicIdentifier":"83892f58-b4b0-4303-8973-4896eed67ce0","userIdentifier":"119ba709-77a3-4b34-92f0-2187ecab7fc5","message":"How are you?"}
因此,对于发送方的两个conn.WriteMessage()调用,我在接收方的conn.ReadMessage()调用中得到一条包含所有数据的消息

我认为这里存在某种竞争条件,因为有时候接收方确实会像预期的那样收到两条单独的消息,但这种情况很少发生


我是否缺少一些基本的东西,或者我只是需要给发送者/接收者打一个额外的电话,一次只处理一条消息

如果消息被缓冲,两条消息同时接收是正常的。问题在于接收端假设一次读取返回一条消息

正如您所看到的,read可能返回多条消息。而且,一条消息可能被拆分为多次读取。后者取决于消息大小。只有您知道消息是什么,以及它是如何分隔的

您必须实现一个返回下一条消息的函数。下面是一个建议的实现,假设消息读取的状态存储在结构中

type MessageParser结构{
buf[]字节
n字节整数
康涅狄格州。。。
}
func NewMessageParser(连接…)MessageParser{
return&MessageParser{
buf:make([]字节,256)//消息大小最长的最佳GES
康涅狄格州:康涅狄格州
}
}
func(m*MessageParser)NextMessage()(字符串,错误){
var nOpenBrakets,位置int
var inString布尔
为了{
//扫描m.buf以查找下一条消息
对于pos0&&m.buf[pos-1]!='\\\'{
inString=false
}
}
位置++
}
//如果消息长度超过缓冲区容量,请增大缓冲区
如果m.nBytes==len(m.buf){
温度:=make([]字节,len(m.buf)*2)
复印件(温度,单位为华氏度)
m、 buf=温度
}
//我们没有找到完整消息,请阅读更多数据
n、 错误:=conn.Read(m.buf[m.nBytes:]
m、 n字节+=n
如果n==0&&err!=nil{
返回“”,错误
}
}
}

如果您查看gorilla WebSockets的写入函数代码

NextWriter returns a writer for the next message to send. The writer's Close
// method flushes the complete message to the network.
读者也有相同的实现。它似乎是正确的。 也许正如@chmike所暗示的,这些消息可能已经被缓冲了

至于实现,您可以始终在消息末尾添加分隔符,并在读取消息时解析消息,直到到达分隔符为止(以防消息溢出)


我试图复制相同的api,但它对我不起作用。在较低的级别上,连接api通常使用c文件编译。您可以尝试使用“-tags netgo”构建应用程序"纯粹使用go来构建它。

使用种族检测器运行应用程序。我尝试过模拟相同的情况。但它对我来说运行得很好。你能发布你的go版本以及gorilla/WebSocket分支/commit吗?go版本:go1.13.5 linux/amd64,在我的go.mod中gorilla被指定为github.com/gorilla/WebSocket v1.4.0,看起来是commit 66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d。我还使用FROM golang:alpine作为我所有容器的基本映像。请参阅ReadJSON()在gorilla api中。如果两个或多个go例程在同一连接上写入,这确实可能是一种竞争条件。是这样吗?写入消息是一个三步过程。打开消息,写入消息数据,关闭消息。如果在关闭之前发生两次写入,两个数据将在同一消息中串联。
NextWriter returns a writer for the next message to send. The writer's Close
// method flushes the complete message to the network.
func writeString(conn *websocket.Conn, data []byte) {
conn.WriteMessage(1, append(data, "\r\n"...))
}