TCP网络编程初步

在学习并发编程之前,先熟悉一个客户端与一个服务端

TCP协议核心机制

TCP(传输控制协议)通过三次握手建立连接,通过四次挥手终止连接,确保数据传输的可靠性。

服务端实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import socket

server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 服务端的socket IPV4 TCP
# 这个只负责接受客户端的连接请求

server_socket.bind(('', 8000))
# 允许最大的等待个数
server_socket.listen(128)
# 接受客户端的连接
socket2, client_addr = server_socket.accept()

while True:
msg = socket2.recv(1024).decode('utf8')
if msg == 'quit':
break
print(f'来自客户端IP:{client_addr[0]},端口号{client_addr[1]}:{msg}')
# 给客户端发送聊天信息
send_msg = input('server>>')
socket2.send(send_msg.encode('utf8'))

socket2.close()
server_socket.close()

客户端实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_addr = ('192.168.100.199', 8000)
client_socket.connect(server_addr)

while True:
send_msg = input('client>>')
if send_msg == 'quit':
client_socket.send(send_msg.encode('utf8'))
break
client_socket.send(send_msg.encode('utf8'))
msg = client_socket.recv(1024).decode('utf8')
print(f'来自服务端IP:{server_addr[0]},端口号{server_addr[1]}:{msg}')

client_socket.close()

TCP三次握手详解

三次握手是TCP建立连接的过程,确保双方都有发送和接收能力。

第一次握手:SYN

1
客户端 → SYN=1, seq=x → 服务端

客户端发送SYN包,seq为随机数x,进入SYN_SENT状态。

第二次握手:SYN+ACK

1
客户端 ← SYN=1, ACK=1, seq=y, ack=x+1 ← 服务端

服务端收到SYN,发送SYN+ACK包:

  • seq为随机数y
  • ack为x+1(确认收到客户端的x)
    服务端进入SYN_RCVD状态。

第三次握手:ACK

1
客户端 → ACK=1, seq=x+1, ack=y+1 → 服务端

客户端发送ACK包:

  • seq为x+1
  • ack为y+1(确认收到服务端的y)
    双方进入ESTABLISHED状态,连接建立。

代码中的三次握手

1
2
3
4
5
# 客户端发起连接(第一次握手)
client_socket.connect(server_addr)

# 服务端接受连接(完成三次握手)
socket2, client_addr = server_socket.accept()

TCP四次挥手详解

四次挥手是TCP断开连接的过程,确保双方数据都传输完毕。

第一次挥手:FIN

1
客户端 → FIN=1, seq=u → 服务端

客户端发送FIN包,seq为u,进入FIN_WAIT_1状态。

第二次挥手:ACK

1
客户端 ← ACK=1, seq=v, ack=u+1 ← 服务端

服务端发送ACK包:

  • ack为u+1(确认收到客户端的FIN)
    服务端进入CLOSE_WAIT状态,客户端进入FIN_WAIT_2状态。

第三次挥手:FIN

1
客户端 ← FIN=1, ACK=1, seq=w, ack=u+1 ← 服务端

服务端发送FIN+ACK包,进入LAST_ACK状态。

第四次挥手:ACK

1
客户端 → ACK=1, seq=u+1, ack=w+1 → 服务端

客户端发送ACK包,进入TIME_WAIT状态(等待2MSL),服务端关闭连接。

代码中的四次挥手

1
2
3
4
5
6
7
8
9
10
11
12
# 客户端发送quit(发起第一次挥手)
client_socket.send('quit'.encode('utf8'))

# 服务端收到quit(第二次挥手)
msg = socket2.recv(1024).decode('utf8')
if msg == 'quit':
break

# 关闭套接字(完成挥手过程)
socket2.close()
server_socket.close()
client_socket.close()

TCP状态转换

建立连接状态

  1. CLOSED:初始状态
  2. SYN_SENT:客户端发送SYN后
  3. LISTEN:服务端调用listen()后
  4. SYN_RCVD:服务端收到SYN后
  5. ESTABLISHED:连接建立完成

断开连接状态

  1. FIN_WAIT_1:客户端第一次挥手
  2. CLOSE_WAIT:服务端第一次收到FIN
  3. FIN_WAIT_2:客户端收到ACK
  4. LAST_ACK:服务端发送FIN
  5. TIME_WAIT:客户端最后等待
  6. CLOSED:连接完全关闭

为什么需要三次握手?

防止旧的重复连接初始化

  • 避免网络延迟导致的旧连接干扰新连接
  • 确保双方都知道对方准备好了

同步序列号

  • 交换初始序列号(ISN)
  • 确保数据按序传输

确认双方能力

  • 确认双方都有发送和接收能力
  • 协商窗口大小等参数

为什么需要四次挥手?

半关闭状态

  • TCP是全双工的,可以单向关闭
  • 服务端可能还有数据要发送

确保数据完整性

  • 等待所有数据都传输完毕
  • 确保没有数据丢失

可靠终止

  • 双方都知道连接要关闭了
  • 防止数据包在网络中”迷路”

TIME_WAIT状态的作用

等待2MSL的原因

  1. 确保最后一个ACK到达:如果服务端没收到ACK会重发FIN
  2. 让旧连接的数据包消失:防止影响新连接
  3. MSL(Maximum Segment Lifetime):报文最大生存时间

实际编程中的影响

连接建立失败

1
2
3
4
try:
client_socket.connect(server_addr)
except ConnectionRefusedError:
print("连接被拒绝:服务端未启动或端口错误")

端口复用问题

1
2
# 避免Address already in use错误
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

关闭

1
2
3
4
5
6
7
8
# 半关闭:关闭发送,仍可接收
client_socket.shutdown(socket.SHUT_WR)

# 等待对方关闭
while True:
data = client_socket.recv(1024)
if not data:
break

总结

三次握手建立可靠连接,四次挥手优雅终止连接,这是TCP协议的核心机制。理解这些过程对于调试网络问题和编写稳定的网络程序非常重要。


在代码中,connect()触发三次握手,close()触发四次挥手,这些细节都由操作系统自动处理。