Go语言TCP编程:从协议原理到完整通信代码实现
本文深入解析Go语言中基于 net 包的TCP编程,涵盖面向连接、可靠交付等核心特性。详细梳理TCP三次握手建立连接与四次挥手释放连接的流程,并结合完整示例代码演示服务端监听、客户端连接及双向数据通信的具体实现方法。
TCP协议核心特性解析
TCP(传输控制协议)是面向连接的运输层协议,其核心优势在于提供可靠、有序的数据传输服务。在使用 net 包进行开发前,理解以下特性至关重要:
- 面向连接:通信前必须建立TCP连接,数据传输完毕后需释放连接,确保资源有效管理。
- 点对点通信:每条TCP连接仅包含两个端点,保证数据传输的针对性。
- 可靠交付:通过序列号和确认机制,确保数据无差错、不丢失、不重复且按序到达。
- 全双工通信:允许通信双方在任一时刻互相发送数据,提高交互效率。
- 面向字节流:TCP将应用层数据视为连续的字节流处理,屏蔽了底层数据包的结构差异。
TCP连接的生命周期
TCP连接的形成与销毁涉及复杂的握手与挥手过程,理解这些状态有助于排查网络连通性问题。
TCP 连接建立:三次握手
传输控制块(TCB)用于存储连接关键信息,如缓冲指针和序列号。假设主机A为客户端,主机B为服务端:
- 第一次握手:服务端创建TCB并进入
LISTEN状态。客户端创建TCB后,发送同步位SYN=1的请求报文段,选择初始序号seq=x。该报文段消耗一个序号但不携带数据,客户端随后进入同步已发送状态。 - 第二次握手:服务端收到请求后,若同意建立连接,则回复确认报文段,将
SYN和ACK置为1,确认号为ack=x+1,并选择自己的初始序号y。服务端进入同步收到状态,同样消耗一个序号。 - 第三次握手:客户端收到确认后,向服务端发送确认报文段,
ACK=1,确认号ack=y+1,序号seq=x+1。此时连接正式建立,双方进入ESTABLISHED状态。
TCP 连接释放:四次挥手
当数据传输完毕,主动关闭方A发送带有 FIN=1 的释放报文段(序号 seq=u),进入 FIN-WAIT-1 状态。服务端B收到后发出确认 ack=u+1,进入 CLOSE-WAIT 半关闭状态,此时A无法发送数据但B仍可发送。B的应用进程通知TCP释放连接后,发送 FIN=1 报文段(序号 w),进入 LAST-ACK 状态。A收到后发出最终确认 ack=w+1,进入 TIME-WAIT 状态。经过 2MSL(最长报文段寿命,通常默认为2分钟)的时间等待后,A彻底进入 CLOSED 状态。
Go语言TCP通信代码实现
基于标准库 net 包,我们可以快速构建可靠的TCP通信应用。首先创建 client 和 server 两个目录以分离逻辑。
第1步:服务端建立监听
在Go中,使用 net.Listen 方法绑定特定端口进行监听。监听成功后,应立即使用 defer listener.Close() 确保程序退出时正确释放资源。
ADDRESS := "127.0.0.1:5000"
listener, err := net.Listen("tcp", ADDRESS)
if err != nil {
fmt.Printf("start tcp server %s failed ,err : %s ", listener, err)
return
}
defer listener.Close()
第2步:客户端发起连接
客户端通过 net.Dial 主动向服务端发起TCP连接。连接建立后,返回的 conn 对象可用于后续的数据读写操作。建议检查错误以确保连接成功建立。
ADDRESS := "127.0.0.1:5000"
conn, err := net.Dial("tcp", ADDRESS) // 主动与服务端建立连接
if err != nil {
fmt.Printf("dial %s failed; err :%s", ADDRESS, err)
return
}
第3步:实现双向数据通信
通信阶段的核心是利用 Read 和 Write 方法在服务端和客户端之间交换数据。为了支持持续交互,通常结合循环结构进行处理。
服务端接收与回复
服务端需要不断等待客户端发来的数据。使用 bufio.NewReader 可以方便地读取用户输入并进行回复,形成简单的交互式聊天功能。
var data [1024]byte
var msg string
reader := bufio.NewReader(os.Stdin)
for {
// 接受来自客户端的信息
n, err := conn.Read(data[:])
if err == io.EOF {
break
}
if err != nil {
fmt.Printf("read from conn failed,err:%s", err)
return
}
fmt.Println("Access Info : ", string(data[:n]))
// 回复信息给客户端
fmt.Print("回复信息:")
msg, _ = reader.ReadString('\n')
msg = strings.TrimSpace(msg)
if msg == "exit" {
break
}
_, _ = conn.Write([]byte(msg))
}
defer conn.Close()
客户端发送与接收
客户端持续获取用户输入并通过 Write 发送至服务端,同时从服务端接收回复内容。设置 “exit” 命令以便优雅退出循环。
for {
fmt.Print("请输入:")
msg, _ := reader.ReadString('\n')
msg = strings.TrimSpace(msg)
if msg == "exit" {
break
}
// 发送数据至服务端
_, _ = conn.Write([]byte(msg))
// 接收服务端的回复
n, err := conn.Read(data[:])
if err == io.EOF {
break
}
if err != nil {
fmt.Println("read from conn failed, err :", err)
return
}
fmt.Println("收到的回复:", string(data[:n]))
}
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









