【问题标题】:Go: Server should block until a message from the client is receivedGo:服务器应该阻塞,直到收到来自客户端的消息
【发布时间】:2015-12-28 09:38:17
【问题描述】:

我正在Go 中构建一些服务器/客户端应用程序(该语言对我来说是新的)。我进行了很多搜索并阅读了一大堆不同的示例,但仍有一件事我找不到。可以说我有一个服务器客户端启动并运行。客户端将向服务器发送某种消息,反之亦然。编解码由包gob完成。

这个例子不是我的应用,它只是一个简单的例子:

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
    "log"
)

type Message struct {
    Sender   string
    Receiver string
    Command  uint8
    Value    int64
}

func (message *Message) Set(sender string, receiver string, command uint8, value int64) *Message {

    message.Sender = sender
    message.Receiver = receiver
    message.Command = command
    message.Value = value

    return message
}

func main() {

    var network bytes.Buffer // Stand-in for a network connection

    enc := gob.NewEncoder(&network) // Will write to network.
    dec := gob.NewDecoder(&network) // Will read from network.

    message := new(Message).Set("first", "second", 10, -1)

    err := enc.Encode(*message) // send message
    if err != nil {
        log.Fatal("encode error:", err)
    }

    var m Message
    err = dec.Decode(&m) // receice message

    if err != nil {
        log.Fatal("decode error:", err)
    }

    fmt.Printf("%q %q %d %d\n", m.Sender, m.Receiver, m.Command, m.Value)
}

这很好用,但我希望服务器在收到新消息之前一直阻塞,这样我就可以将接收进程放入 infinite for loopgoroutine 内。

类似的东西:

for {
    // The server blocks HERE until a message from the client is received

    fmt.Println("Received message:")

    // Decode the new message
    var m Message
    err = dec.Decode(&m) // receice message

    if err != nil {
        log.Fatal("decode error:", err)
    }

    fmt.Printf("%q %q %d %d\n", m.Sender, m.Receiver, m.Command, m.Value)
}

【问题讨论】:

  • 那么问题是如何与golang建立网络连接?
  • 不,问题是如何在解码连接缓冲区之前等待消息。在 golang 中很容易设置 TCP 服务器,但我不知道如何在一侧阻止消息接收过程,因此上面的 for 循环不会打印数千个错误,直到它在 net.Conn 上找到新消息。
  • 假设来自客户端的消息何时到达是未知的,因此服务器将阻塞并在 goroutine 内等待新消息,但我不知道如何实现这一点。

标签: go server client


【解决方案1】:

gob 解码器会一直阻塞,直到它读取完整的消息或出现错误。问题中的读取循环按原样工作。

working example on the playground

【讨论】:

    【解决方案2】:

    将长度标头添加到原始 tcp 流。

    这意味着,在发送实际负载之前,向服务器发送一个 4 字节长度的标头信息。并在服务器端读取 4 个字节,分配缓冲区,完整读取总消息,最后解码。

    假设你有一个 tcp 连接 conn,在服务器端我们可以有:

    func getInt(v []byte) int {
        var r uint
        r = 0
        r |= uint(v[0]) << 24
        r |= uint(v[1]) << 16
        r |= uint(v[2]) << 8
        r |= uint(v[3]) << 0
        return int(r)
    }
    
    buf := make([]byte, 4)
    _, err := io.ReadFull(conn, buf)
    if err != nil {
        return
    }
    
    length := getInt(buf)
    buf = make([]byte, length)
    _, err = io.ReadFull(conn, buf)
    if err != nil {
        return
    }
    //do gob decode from `buf` here
    

    您可能知道客户端参考我认为的服务器端源。

    【讨论】:

    • 这是如何等待未来的消息?我想要这样的东西:stackoverflow.com/a/23529698/4572536 但使用原始 tcp 连接和gob 作为 en-/decoder。再说一次,Go 对我来说是新的,所以我需要一些可以学习的工作示例。
    • conn.Read 是否等到它可以填满缓冲区?
    • 不,但实际上,如果客户端发送的字节数少于 4 个字节,您可能不会收到少于 4 个字节的数据。严格来说,我们也将ReadFull 用于前 4 个字节。
    • 所以我假设我应该将此代码(其中的一部分)粘贴在 goroutine 内的 for 循环中以获取特定连接,并使用 continue 而不是 return。这是等待消息的好机制吗?
    • 使用 gob 时不需要长度前缀。如果您确实想为二进制整数添加前缀,则可以使用“编码/二进制”。 (并且网络字节序一般定义为BigEndian)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-11-02
    • 1970-01-01
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多