duanchun5520 2016-06-28 20:07 采纳率: 0%
浏览 123
已采纳

Golang-将两条Reader返回的行用于select语句

For a simple golang chat/telnet client, I want to pass strings received from two bufio Readers to a select statement, so I can either send the user input to the server, or print the server sent data.

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

The compiler gives me back this error

select case must be receive, send or assign recv

I suspect I should be using channels. But

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('
')
    i, _ := connReader.ReadString('
')
    outgoing <- o
    incoming <- i

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

But the code doesn't accept or receive data. Finally, I suspect I should be using two go routines to check for the Reader return value?

  • 写回答

1条回答 默认 最新

  • doutangtan6386 2016-06-28 20:20
    关注

    You need to run them in goroutines or they can't simultaneously occur:

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

    Edit

    To further elaborate: your first example won't work because select requires each case to be either a channel read or channel send operation, and the function call bufio.Reader.ReadString() is neither.

    Your second example won't work because the line outgoing <- o will block indefinitely. It is trying to send o over the channel outgoing, but outgoing is not buffered, and nothing is listening on it. Furthermore, since neither ReadString() call will return until a line is read, your for loop will only proceed once a line is read from BOTH readers in turn (and will then block on the channel send).

    That's why you need each Reader to have its own goroutine, so each can independently read off its Reader as input allows, and store it to the relevant channel as lines are read, and your select statement can then react to each line as it comes in from the individual goroutines.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?