Exercise 8.3: In the netcat3 example, although conn is a value of interface type, its underlying real type is *net.TCPConn, which represents a TCP connection. A TCP connection has two parts, read and write, which can be closed using the CloseRead and CloseWrite methods, respectively. Modify the main goroutine code of netcat3 to only close the part written in the network connection, so that the background goroutine can continue to print the data returned from the reverb1 server after the standard input is closed. (It is more difficult to do the same on the reverb2 server; see exercise 8.4.)
1.
net.Dial()
func Dial(network, address string) (Conn, error)
2.net.TCPConn
type TCPConn struct {
// contains filtered or unexported fields
}
TCPConn is an implementation of the Conn interface for TCP network connections.
package main import ( "io" "log" "net" "os" ) func main() { conn, err := net.Dial("tcp", "localhost:8040") if err != nil { log.Fatal(err) } //The built-in make function creates a channel that can send data of type struct done := make(chan struct{}) //go statement calls a function literal, the usual form of starting a goroutine go func() { //Connect to standard output from the network, and block if the connection is not broken //If the TCP read connection is closed, an error will be reported: use of closed network connection _, err := io.Copy(os.Stdout, conn) log.Println(err) log.Println("done") //Send channel to receiving goroutine done <- struct{}{} }() //From the standard input to the network connection, this place will block, press Control+D to close the standard input mustCopy(conn, os.Stdin) // conn.Close() //Type assertion, calling the *net.TCPConn method CloseWrite() only closes the TCP write connection cw := conn.(*net.TCPConn) cw.CloseWrite() <-done // Block waiting for the background goroutine to finish receiving the channel } func mustCopy(dst io.Writer, src io.Reader) { if _, err := io.Copy(dst, src); err != nil { log.Fatal(err) } }