Golang中TCPConn类型的closeWrite方法关闭了双向连接而非单向连接
Golang中TCPConn类型的closeWrite方法关闭了双向连接而非单向连接 我正在阅读《Go程序设计语言》这本书,并在网上寻求帮助后,为练习8.3编写了代码。当我需要创建一个客户端,使其在按下Ctrl+D时关闭输入,并等待服务器写入其响应后再退出程序时,按下Ctrl+D后它并没有等待服务器。
以下是我的代码:
服务器代码如下:
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
func echo(conn net.Conn, shout string, delay time.Duration){
fmt.Fprintln(conn, "\t", strings.ToUpper(shout))
time.Sleep(delay)
fmt.Fprintln(conn, "\t", shout)
time.Sleep(delay)
fmt.Fprintln(conn, "\t", strings.ToLower(shout))
}
func handleConn(conn net.Conn) {
input := bufio.NewScanner(conn)
for input.Scan() {
go echo(conn, input.Text(), 1*time.Second)
}
conn.Close()
}
客户端代码如下:
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
addr, err := net.ResolveTCPAddr("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
conn, err := net.DialTCP("tcp", nil, addr)
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, conn)
log.Print("done")
done<- struct{}{}
}()
mustCopy(conn, os.Stdin)
err = conn.CloseWrite()
if err != nil {
log.Fatal(err)
}
<-done
}
func mustCopy(dst io.Writer, src io.Reader){
_, err := io.Copy(dst, src)
if err != nil {
log.Fatal(err)
}
}
以下是控制台输入和输出:
A A A a
B B B
A A b A a
A A
CTRL + D 2021/05/16 04:30:29 done
我使用的是Linux系统。我真的尝试过了,但我不明白为什么按下Ctrl+D后,它没有等待echo操作完成。
更多关于Golang中TCPConn类型的closeWrite方法关闭了双向连接而非单向连接的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中TCPConn类型的closeWrite方法关闭了双向连接而非单向连接的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
根据你的代码和描述,问题在于对CloseWrite()方法的理解。CloseWrite()方法确实只关闭了写入方向的连接,但服务器端的bufio.Scanner在检测到EOF时会立即退出循环,导致连接被完全关闭。
以下是关键问题的分析:
-
服务器端:
bufio.NewScanner(conn)会持续读取数据直到遇到EOF。当客户端调用CloseWrite()后,服务器端会收到EOF,导致input.Scan()返回false,从而退出循环并执行conn.Close()。 -
客户端:调用
CloseWrite()后,虽然只关闭了写入方向,但服务器端已经关闭了整个连接,所以io.Copy(os.Stdout, conn)会立即读取到EOF并退出。
要解决这个问题,需要修改服务器端代码,使其在读取到EOF后继续处理未完成的echo协程。以下是修改后的服务器代码示例:
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
"sync"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
func echo(conn net.Conn, shout string, delay time.Duration, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Fprintln(conn, "\t", strings.ToUpper(shout))
time.Sleep(delay)
fmt.Fprintln(conn, "\t", shout)
time.Sleep(delay)
fmt.Fprintln(conn, "\t", strings.ToLower(shout))
}
func handleConn(conn net.Conn) {
var wg sync.WaitGroup
input := bufio.NewScanner(conn)
for input.Scan() {
wg.Add(1)
go echo(conn, input.Text(), 1*time.Second, &wg)
}
// 等待所有echo协程完成
wg.Wait()
conn.Close()
}
主要修改:
- 使用
sync.WaitGroup来跟踪所有echo协程 - 在
handleConn函数中,读取循环结束后调用wg.Wait()等待所有echo协程完成 - 在所有echo协程完成后才关闭连接
这样修改后,当客户端调用CloseWrite()时,服务器端会等待所有已启动的echo协程完成后再关闭连接,客户端就能收到完整的响应了。

