【问题标题】:Golang - use two Reader returned lines for select statementGolang - 使用两条 Reader 返回的行进行选择语句
【发布时间】:2016-10-31 07:42:03
【问题描述】:

对于一个简单的 golang 聊天/远程登录客户端,我想将从两个 bufio 阅读器接收到的字符串传递给一个 select 语句,这样我就可以将用户输入发送到服务器,或者打印服务器发送的数据。

conn, _ := net.Dial("tcp", "localhost:8998")
for {
    select{
    case line, _ := bufio.NewReader(os.Stdin).ReadString('\n'):
        fmt.Print("> ")
        fmt.Fprintf(conn, line + "\n")
    case data, _ := bufio.NewReader(conn).ReadString('\n'):
        fmt.Print(data)
    }
}

编译器返回这个错误

select case must be receive, send or assign recv

我怀疑我应该使用频道。但是

conn, _ := net.Dial("tcp", "localhost:8998")
outgoing := make(chan string)
incoming := make(chan string)
for {
    inputReader := bufio.NewReader(os.Stdin)
    connReader := bufio.NewReader(conn)

    o, _ := inputReader.ReadString('\n')
    i, _ := connReader.ReadString('\n')
    outgoing <- o
    incoming <- i

    select{
    case out := <-outgoing:
        fmt.Print("> ")
        fmt.Fprintf(conn, out + "\n")
    case in := <-incoming:
        fmt.Print(in)
    }
}

但代码不接受或接收数据。最后,我怀疑我应该使用两个 go 例程来检查 Reader 返回值?

【问题讨论】:

    标签: select go client chat


    【解决方案1】:

    您需要在 goroutine 中运行它们,否则它们不能同时发生:

    conn, _ := net.Dial("tcp", "localhost:8998")
    
    // Make outgoing reader routine
    outgoing := make(chan string)
    go func() {
        inputReader := bufio.NewReader(os.Stdin)
        for {
            o, err := inputReader.ReadString('\n')
            if err != nil {
                fmt.Printf("outgoing error: %v", err)
                return
            }
            outgoing <- o
        }
    }()
    
    // Make incoming reader routine
    incoming := make(chan string)
    go func() {
        connReader := bufio.NewReader(conn)
        for {
            i, err := connReader.ReadString('\n')
            if err != nil {
                fmt.Printf("incoming error: %v", err)
                return
            }
            incoming <- i
        }
    }()
    
    for {
        select {
        case out := <-outgoing:
            fmt.Print("> ")
            fmt.Fprintf(conn, out+"\n")
        case in := <-incoming:
            fmt.Print(in)
        }
    }
    

    编辑

    进一步说明:您的第一个示例不起作用,因为select 要求每种情况都是通道读取或通道发送操作,而函数调用bufio.Reader.ReadString() 两者都不是。

    您的第二个示例不起作用,因为outgoing &lt;- o 行将无限期阻塞。它试图通过通道outgoing 发送o,但outgoing 没有被缓冲,并且没有任何东西在监听它。此外,由于在读取一行之前,ReadString() 调用都不会返回,因此您的 for 循环只会在依次从 BOTH 阅读器读取一行后继续进行(然后将阻塞通道发送)。

    这就是为什么您需要每个 Reader 都有自己的 goroutine,因此每个 Reader 都可以在输入允许的情况下独立读取其 Reader,并在读取行时将其存储到相关通道,然后您的 select 语句可以对每个响应行,因为它来自各个 goroutine。

    【讨论】:

    • 你把 goroutines 放在了 for 循环之外。这是否意味着 goroutines 不会关闭?并且通道不断被填充,就好像它是一个 for 循环?当我写出这个时,客户端只有在我将 goroutines 放入 for 循环时才能工作。
    • 好吧,没有必要在每次循环迭代时重新创建 goroutine,因为这也涉及重新创建阅读器,并且您会留下很多孤立的例程。请注意,显示的 goroutines 中有一个 for 循环,因此它们无限循环,在 Reader 读取时填充通道。如果要关闭它们,最佳方法是使用所有 3 个(两个 goroutine 和主例程)都监听的退出通道,并在退出通道关闭时中止它们的 for 循环(通常通过return)。示例:play.golang.org/p/vxK6kU21Ya
    • 感谢@Kaedys,这是一个很好的例子,说明了如何解决和使用通道来阻止例程。
    猜你喜欢
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-13
    • 2013-08-16
    相关资源
    最近更新 更多