Go的net包通过抽象套接字操作,提供简洁高效的TCP编程接口,其核心在于net.Listener和net.Conn的封装,结合goroutine实现高并发连接处理,使网络编程更直观可靠。

Go语言的
net
net
要理解Go的
net
TCP服务器端示例:
package main
import (
"bufio"
"fmt"
"log"
"net"
"strings"
"time" // 引入time包用于模拟处理延迟
)
func handleConnection(conn net.Conn) {
defer func() {
log.Printf("连接 %s 已关闭。", conn.RemoteAddr().String())
conn.Close() // 确保连接最终被关闭
}()
log.Printf("接收到来自 %s 的新连接。", conn.RemoteAddr().String())
reader := bufio.NewReader(conn)
for {
// 设置一个读取超时,防止客户端不发送数据导致永久阻塞
conn.SetReadDeadline(time.Now().Add(5 * time.Minute))
message, err := reader.ReadString('\n')
if err != nil {
// 常见的错误如 EOF (客户端关闭连接) 或 timeout
if err.Error() == "EOF" {
log.Printf("客户端 %s 关闭了连接。", conn.RemoteAddr().String())
} else if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Printf("连接 %s 读取超时,即将关闭。", conn.RemoteAddr().String())
} else {
log.Printf("读取错误 %s: %v", conn.RemoteAddr().String(), err)
}
return // 遇到错误,终止此连接的处理
}
trimmedMessage := strings.TrimSpace(message)
log.Printf("收到来自 %s 的消息: %s", conn.RemoteAddr().String(), trimmedMessage)
// 模拟一些处理延迟
time.Sleep(1 * time.Second)
// 回复客户端
response := fmt.Sprintf("服务器收到你的消息: '%s',已处理。\n", trimmedMessage)
_, err = conn.Write([]byte(response))
if err != nil {
log.Printf("写入错误 %s: %v", conn.RemoteAddr().String(), err)
return
}
}
}
func main() {
listenAddr := ":8080"
listener, err := net.Listen("tcp", listenAddr)
if err != nil {
log.Fatalf("无法监听 %s: %v", listenAddr, err)
}
defer listener.Close()
log.Printf("TCP 服务器正在监听 %s", listenAddr)
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("接受连接错误: %v", err)
continue // 继续尝试接受下一个连接
}
go handleConnection(conn) // 为每个新连接启动一个goroutine
}
}TCP客户端示例:
立即学习“go语言免费学习笔记(深入)”;
package main
import (
"bufio"
"fmt"
"log"
"net"
"os"
"strings"
"time"
)
func main() {
serverAddr := "localhost:8080"
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
log.Fatalf("无法连接到服务器 %s: %v", serverAddr, err)
}
defer func() {
log.Println("客户端连接已关闭。")
conn.Close()
}()
log.Printf("已连接到服务器 %s", serverAddr)
// 启动一个goroutine来接收服务器响应
go func() {
reader := bufio.NewReader(conn)
for {
// 同样设置读取超时,防止服务器不回复导致阻塞
conn.SetReadDeadline(time.Now().Add(1 * time.Minute))
message, err := reader.ReadString('\n')
if err != nil {
if err.Error() == "EOF" {
log.Println("服务器关闭了连接。")
} else if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Println("读取服务器响应超时,即将退出。")
} else {
log.Printf("读取服务器响应错误: %v", err)
}
return
}
fmt.Printf("收到服务器响应: %s", strings.TrimSpace(message))
}
}()
// 从标准输入读取用户消息并发送给服务器
scanner := bufio.NewScanner(os.Stdin)
fmt.Println("请输入消息,按回车发送。输入 'exit' 退出。")
for scanner.Scan() {
input := scanner.Text()
if strings.ToLower(input) == "exit" {
break
}
_, err := conn.Write([]byte(input + "\n")) // 记得加换行符,因为服务器是按行读取的
if err != nil {
log.Printf("发送消息错误: %v", err)
break
}
}
if err := scanner.Err(); err != nil {
log.Printf("读取标准输入错误: %v", err)
}
}这两个例子展示了TCP通信的基石:服务器通过
net.Listen
Accept
net.Dial
net.Conn
net
net
从我的经验来看,
net
net.Listener
net.Conn
net.Listener
Accept()
net.Conn
net.Conn
Read
Write
它还提供了像
net.Dial
net
error
Go语言的并发模型,特别是goroutine和channel,在处理多客户端并发连接时简直是如鱼得水。在TCP服务器场景中,当
net.Listener
Accept()
// 服务器主循环片段
for {
conn, err := listener.Accept()
if err != nil {
log.Printf("接受连接错误: %v", err)
continue
}
go handleConnection(conn) // 关键:为每个连接启动一个独立的goroutine
}这里
go handleConnection(conn)
handleConnection
想象一下,如果服务器是单线程的,当一个客户端连接过来,服务器在处理它的请求时,其他客户端就必须等待,直到前一个请求处理完毕。这在实际应用中是完全不可接受的。而Go的goroutine非常轻量级,启动一个goroutine的开销远小于操作系统线程,这使得服务器能够轻松地同时处理成千上万个并发连接,而不会耗尽系统资源。
此外,Go的并发模型还体现在其内存管理上。每个goroutine都有自己的栈空间,并且这个栈是动态伸缩的,这进一步降低了资源消耗。当需要goroutine之间进行通信时,Go提供了channel,它是一种类型安全的通信机制,鼓励“通过通信共享内存,而不是通过共享内存来通信”的哲学。虽然在简单的TCP服务器中,可能每个
handleConnection
在TCP网络编程中,错误处理和调试是构建稳定服务的基石,尤其是在Go语言这种强制错误处理的语境下。我个人在实践中总结了一些模式和技巧:
1. 立即检查错误并处理: 这是Go语言最基本的错误处理哲学。在每次可能返回错误的操作后,都要立即检查
err != nil
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
// 这里的错误通常是网络不通、服务器未启动或地址错误
log.Fatalf("无法连接到服务器: %v", err) // 使用Fatalf直接退出,因为无法继续
}
defer conn.Close() // 重要的资源清理
// 读写操作
_, err = conn.Write(data)
if err != nil {
// 写入错误可能意味着连接已断开、缓冲区满等
log.Printf("写入数据错误: %v", err)
return // 或者根据业务逻辑选择重试、关闭连接等
}2. 区分错误类型:
net
net.Error
netErr.Temporary()
netErr.Timeout()
message, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF { // 客户端正常关闭连接
log.Println("客户端关闭了连接。")
} else if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
log.Println("读取操作超时。")
} else {
log.Printf("读取发生未知错误: %v", err)
}
return
}io.EOF
3. defer conn.Close()
handleConnection
defer conn.Close()
4. 设置读写超时: 网络通信中最怕的就是阻塞。客户端或服务器如果长时间不发送数据,
Read
conn.SetReadDeadline
conn.SetWriteDeadline
conn.SetReadDeadline(time.Now().Add(5 * time.Minute)) // 5分钟内没有数据就超时
5. 日志记录: 清晰、有用的日志是调试的生命线。记录连接的建立、关闭、收到的消息、发送的消息以及所有遇到的错误。使用
log
6. 使用netstat
lsof
netstat -ano
netstat -natp
lsof -i :端口号
7. 逐步调试与打印: 虽然Go的调试工具(如Delve)越来越好用,但在网络编程中,有时简单的
fmt.Println
log.Println
8. 模拟网络异常: 在开发和测试阶段,尝试模拟网络断开、延迟、丢包等情况,以测试你的错误处理逻辑是否健壮。这可以通过一些网络工具(如
tc
通过这些模式和技巧,你就能更好地驾驭Go的
net
以上就是Golang网络编程基础 net包TCP示例的详细内容,更多请关注php中文网其它相关文章!
编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号