0%

TCP协议学习

TCP数据传输

一、TCP简介

1、什么是TCP协议

TCP是面向连接的协议,这是因为在一个应用进程可以开始向另一个应用进程发送数据之前,这两个进程必须先相互“握手”,即它们必须相互发送某些预备报文段,以建立确保数据传输的参数。它有以下几个特点:

  • 面向连接:TCP一定是“一对一”的,无法做到一对多;
  • 可靠:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端(当然不是说绝对可靠);
  • 基于字节流:消息是“没有边界”的,所以无论我们消息有多大都可以进行传输。并且消息是“有序的”,当前一个消息没有收到的时候,即使它先收到了后面的字节已经收到,那么也不能扔给应用层去处理,同时对重复的报文会自动丢弃。

2、为什么需要TCP协议

IP层是“不可靠”的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中数据的完整性。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。

TCP

3、TCP头部格式

TCP头部的最小长度是 20字节。这是基本的TCP头部,不包含任何选项字段。TCP头部的最大长度是 60字节。当数据取最大32位时,TCP头部最大。

源端口号(16位) 目标端口号(16位)
序列号(32位)
确认应答号(32位)
首部长度(4位) 保留(6位) URG ACK PSH RST SYN FIN 窗口大小(16位)
校验和(16位) 紧急指针(16位)
选项(长度可变)
数据
表 1 TCP头部格式
各部分说明:
  • 源端口(Source Port):16位,发送方的端口号;
  • 目标端口(Destination Port):16位,接收方的端口号;
  • 序列号(Sequence Number):32位,发送数据段的序列号,用于数据重组;
  • 确认号(Acknowledgment Number):32位,期望接收的数据段的下一个序列号,用于确认接收的数据;
  • 数据偏移(Data Offset):4位,表示TCP头部的长度,以32位字(4字节)为单位;
  • 保留位(Reserved):6位,保留为将来使用,通常置为0;
  • 控制位(Control Flags):6位,包括以下标志:
    1. URG(紧急指针有效);
    2. ACK(确认号有效);
    3. PSH(推送数据);
    4. RST(重置连接);
    5. SYN(同步序列号,用于建立连接);
    6. FIN(终止连接)。
  • 窗口大小(Window Size):16位,表示接收方允许发送方发送的数据量(以字节为单位);
  • 校验和(Checksum):16位,用于检验数据的完整性;
  • 紧急指针(Urgent Pointer):16位,指出紧急数据的结束位置,仅当URG标志置位时有效;
  • 选项(Options):可变长度,包含额外的选项,如时间戳、窗口扩展等;
  • 数据(Padding):可变长度,用于填充到32位边界。

二、Python传输TCP数据

用Python实现TCP数据传输需要从服务端(server)和客户端(client)两个部分来实现。

1、 服务端(server)

思路流程:

  • 创建TCP套接字;
  • 绑定套接字到指定的地址和端口;
  • 使套接字开始监听连接请求;
  • 接受新的连接;
  • 接收来自客户端的数据;
  • 发送确认消息回给客户端;
  • 关闭和客户端的连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import socket


def start_server(host, port):
# 创建一个TCP套接字
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定套接字到指定的地址和端口
server_socket.bind((host, port))

# 使套接字开始监听连接请求,最多可以有 5 个未决连接
server_socket.listen(5)

print(f"Server listening on {host}:{port}")

while True:
# 接受一个新的连接
client_socket, client_address = server_socket.accept()
print(f"Connection from {client_address}")

# 接收来自客户端的数据,最多接收 1024 字节
data = client_socket.recv(1024).decode()
print(f"Received data: {data}")

# 发送确认消息回给客户端
client_socket.sendall("Data received".encode())

# 关闭和客户端的连接
client_socket.close()


if __name__ == "__main__":
start_server('127.0.0.1', 11925)

2、客户端(client)

思路流程:

  • 创建TCP套接字;
  • 连接到指定的服务器地址和端口;
  • 编辑需要发送的数据;
  • 发送数据给服务端;
  • 接收来自服务端的响应数据;
  • 关闭和服务端的连接。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import socket


def start_client(host, port):
# 创建一个 TCP 套接字
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接到指定的服务器地址和端口
client_socket.connect((host, port))

# 要发送的消息
message = "hello world!"

# 发送消息给服务器
client_socket.sendall(message.encode())

# 接收来自服务器的响应数据,最多接收 1024 字节
data = client_socket.recv(1024).decode()
print(f"Received from server: {data}")

# 关闭与服务器的连接
client_socket.close()


if __name__ == "__main__":
start_client('127.0.0.1', 11925)