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('\n'):
fmt.Print("> ")
fmt.Fprintf(conn, line + "\n")
case data, _ := bufio.NewReader(conn).ReadString('\n'):
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('\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)
}
}
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?
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, 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)
}
}
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.