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)
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
| client_socket.send('quit'.encode('utf8'))
msg = socket2.recv(1024).decode('utf8') if msg == 'quit': break
socket2.close() server_socket.close() client_socket.close()
|
TCP状态转换
建立连接状态
- CLOSED:初始状态
- SYN_SENT:客户端发送SYN后
- LISTEN:服务端调用listen()后
- SYN_RCVD:服务端收到SYN后
- ESTABLISHED:连接建立完成
断开连接状态
- FIN_WAIT_1:客户端第一次挥手
- CLOSE_WAIT:服务端第一次收到FIN
- FIN_WAIT_2:客户端收到ACK
- LAST_ACK:服务端发送FIN
- TIME_WAIT:客户端最后等待
- CLOSED:连接完全关闭
为什么需要三次握手?
防止旧的重复连接初始化
- 避免网络延迟导致的旧连接干扰新连接
- 确保双方都知道对方准备好了
同步序列号
确认双方能力
为什么需要四次挥手?
半关闭状态
- TCP是全双工的,可以单向关闭
- 服务端可能还有数据要发送
确保数据完整性
可靠终止
- 双方都知道连接要关闭了
- 防止数据包在网络中”迷路”
TIME_WAIT状态的作用
等待2MSL的原因
- 确保最后一个ACK到达:如果服务端没收到ACK会重发FIN
- 让旧连接的数据包消失:防止影响新连接
- MSL(Maximum Segment Lifetime):报文最大生存时间
实际编程中的影响
连接建立失败
1 2 3 4
| try: client_socket.connect(server_addr) except ConnectionRefusedError: print("连接被拒绝:服务端未启动或端口错误")
|
端口复用问题
1 2
| 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()触发四次挥手,这些细节都由操作系统自动处理。