Golang读取TCP套接字时卡住怎么办
Golang读取TCP套接字时卡住怎么办 大家好,我对Go语言以及套接字的底层细节都非常不熟悉。希望大家能帮助我。
我有一个在计算机集群上运行的客户端-服务器应用程序。服务器运行在任意一个系统上,它会连接到多个进程,并在应用程序的整个生命周期内保持这些连接处于打开状态。每当客户端连接到服务器时,服务器会从该连接读取一行数据。这个过程在连接到130个客户端进程时运行顺畅。但当有131个客户端进程时,大多数情况下都会失败。服务器会卡在尝试读取那一行数据的地方。相关代码如下所示。
服务器
func main() {
// 一些代码
port := 8080
host := "0.0.0.0"
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
utils.CheckError(err)
log.Printf("Server running on port %d\n", port)
for {
conn, err := ln.Accept()
utils.CheckError(err)
handleConnection(conn)
}
// 其他代码
}
func handleConnection(c net.Conn) {
status, err := bufio.NewReader(c).ReadString('\n') // 服务器就是在这里卡住了!!!
utils.CheckError(err)
// 其他代码
}
客户端
func main() {
// 一些代码
conn, err := net.Dial("tcp", os.Args[1])
utils.CheckError(err)
filename := os.Args[2]
// 一些代码
f, err := os.Open(pdFilename)
utils.CheckError(err)
line, err := bufio.NewReader(f).ReadString('\n')
utils.CheckError(err)
fmt.Fprintf(conn, "%s", line)
// 其他代码
}
更多关于Golang读取TCP套接字时卡住怎么办的实战教程也可以访问 https://www.itying.com/category-94-b0.html
该示例仅在 goroutine 中处理单个请求,以便更快地响应请求。我不认为串行处理会导致我遇到的这种行为。
更多关于Golang读取TCP套接字时卡住怎么办的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
@samvidmistry 别忘了关闭连接 conn.close()。
同时要确保客户端确实在发送数据——你是否监控了客户端和服务器端的所有错误(如果有的话)?
在服务器编程中,实际上并不存在任何串行操作(即使看起来像),而是并发的,因为无法保证请求会按顺序发生。因此,在您未来的所有开发中都必须考虑到这一点。
正如以下示例所示,在服务器端的并发环境中,您应该使用一个 goroutine 来处理结果。
https://golang.org/src/net/example_test.go
感谢您的帮助。问题出在客户端。它卡在了其他一些代码中,无法向服务器发送数据,因此服务器也卡住了。这个错误非常严重,我甚至不知道程序是如何在两个进程下运行的,更不用说130个了。再次重申,一个串行接收的服务器本身应该不是问题。另外,我该如何关闭这个问题?
你好,Samvid,也许可以尝试添加类似这样的代码:
import "runtime/debug"
/* ... */
func main() {
/* ... */
go func() {
time.Sleep(5 * time.Second)
debug.PrintStack()
}()
/* ... */
}
我认为这应该会打印出 goroutine 的堆栈跟踪信息,这样你就能看到它们在做什么了。
好的,感谢你的专业建议。我确实在 handleConnection 函数后面启动了一个 goroutine。我原本希望能了解一下这段代码可能有什么问题。我认为当进程数量超过130个时,串行处理请求并不是导致代码行为异常的原因。我所说的串行,并不是指任何特定的顺序。我知道请求可以以任何顺序到达。但我是一个接一个地处理它们的。
我无法理解为什么在大约130个请求时会发生这种情况,但这并不是重点。基本上,并发意味着同时运行。因此,如果两个或更多请求几乎在同一时间触及 ln.Accept(),那么在接下来的时刻,你必须为每个请求跳转到一个新的goroutine来处理结果。如果没有这些goroutine,你将会有多个请求同时调用相同的代码,导致不可预测的结果。
func main() {
fmt.Println("hello world")
}
如果没有这些 goroutine,同一时间许多请求将调用相同的代码,导致不可预测的结果。
这种说法不正确。非并发的 TCP 服务器并非坏事,它应该可以正常工作。是的,这有缺点,但它不会产生“不可预测的结果”。如果因为服务器无法跟上负载而导致太多客户端在等待 .Accept(),那么它们可能会被取消。
请再次检查所有返回的错误,并确保 utils.CheckError(err) 在出现错误时能让进程失败。别忘了关闭所有连接。
func main() {
fmt.Println("hello world")
}
问题出在服务器没有为每个连接启动独立的goroutine,导致并发处理能力受限。当连接数超过一定数量时,会阻塞在单个连接的读取操作上。
解决方案是使用goroutine并发处理连接:
func handleConnection(c net.Conn) {
go func(conn net.Conn) {
defer conn.Close()
status, err := bufio.NewReader(conn).ReadString('\n')
if err != nil {
log.Printf("Read error: %v", err)
return
}
// 处理数据
fmt.Printf("Received: %s", status)
}(c)
}
更完整的服务器示例:
func main() {
port := 8080
host := "0.0.0.0"
ln, err := net.Listen("tcp", fmt.Sprintf("%s:%d", host, port))
utils.CheckError(err)
defer ln.Close()
log.Printf("Server running on port %d\n", port)
for {
conn, err := ln.Accept()
if err != nil {
log.Printf("Accept error: %v", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
reader := bufio.NewReader(conn)
for {
status, err := reader.ReadString('\n')
if err != nil {
if err != io.EOF {
log.Printf("Read error: %v", err)
}
return
}
// 处理接收到的数据
fmt.Printf("Received: %s", status)
}
}
客户端也需要确保正确关闭连接:
func main() {
conn, err := net.Dial("tcp", os.Args[1])
utils.CheckError(err)
defer conn.Close()
filename := os.Args[2]
f, err := os.Open(filename)
utils.CheckError(err)
defer f.Close()
line, err := bufio.NewReader(f).ReadString('\n')
utils.CheckError(err)
_, err = fmt.Fprintf(conn, "%s", line)
utils.CheckError(err)
}
关键点:
- 每个连接使用独立的goroutine处理
- 确保连接在使用后正确关闭
- 添加错误处理避免goroutine泄漏
- 使用循环读取以处理多个消息

