TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP协议在建立连接时,会采用一种称为“三次握手”的过程来确保双方的通信能力和理解能力,从而建立起可靠的传输连接。
一、TCP三次握手的含义
TCP三次握手(Three-Way Handshake)是指在建立TCP连接时,需要通过交换三个不同的数据包来确认通信双方的序列号和窗口大小,以确保双方的通信能力和理解能力。这一过程作为可靠性传输的基础,确保了数据的可靠传输和完整性。
二、TCP三次握手的过程
TCP三次握手的过程可以分为以下三个阶段:
1. 第一次握手(SYN)
客户端向服务器发送一个带有SYN(同步)标志的数据包,表明客户端请求建立连接,并告知服务器自己的初始序列号。此时,客户端进入SYN_SENT状态。
在这一步中,客户端会构造一个TCP报文,其中的标记位为SYN,表示请求建立新连接。同时,客户端会选择一个初始序列号(Seq=X,X一般为1),并将其包含在报文中。随后,客户端进入SYN_SENT阶段,等待服务器的响应。
2. 第二次握手(SYN ACK)
服务器接收到客户端的SYN数据包后,会回复一个带有SYN/ACK(同步/确认)标志的数据包给客户端。该数据包中,SYN位用于指明服务器收到了客户端的请求,并且服务器也想和客户端建立连接;ACK位则确认了服务器收到了客户端的请求。同时,服务器会为建立的连接分配资源,并指定自己的初始序列号。此时,服务器进入SYN_RECV状态。
在这一步中,服务器会构造一个TCP报文,其中的标记位为SYN和ACK,表示确认客户端的报文Seq序号有效,服务器能正常接收客户端发送的数据,并同意创建新连接。同时,服务器会选择一个初始序列号(Seq=y),并将其包含在报文中。此外,服务器还会将客户端的序列号加1作为确认号(Ack=x 1),表示收到客户端的序号Seq并将其值加1作为自己确认号Ack的值。随后,服务器进入SYN_RCVD阶段,等待客户端的确认。
3. 第三次握手(ACK)
客户端接收到服务器的SYN/ACK数据包后,会向服务器发送一个带有ACK(确认)标志的数据包。该数据包中,ACK位用于确认服务器收到了客户端的同步请求。同时,客户端会在该数据包中向服务器传递自己分配到的初始序列号。此时,客户端进入ESTABLISHED(已建立连接)状态。服务器收到客户端的ACK数据包后,也进入ESTABLISHED状态。至此,TCP连接建立成功,可以开始进行数据的传输。
在这一步中,客户端会构造一个TCP报文,其中的标记位为ACK,表示确认收到服务器同意连接的信号。同时,客户端会将服务器的序列号加1作为自己的确认号(Ack=y 1),并将自己的序列号加1(Seq=x 1)作为报文的序列号。随后,客户端进入ESTABLISHED阶段,表示连接已经建立。服务器收到来自客户端的确认报文后,也进入ESTABLISHED阶段,表示连接已经建立成功。
三、TCP三次握手的重要性
TCP三次握手的过程确保了通信双方的可靠和完整性。通过交换三个数据包,客户端和服务器验证了彼此的通信能力,并约定了初始的序列号和窗口大小,以确保后续数据的可靠传输。
- 确认通信能力:通过三次握手,客户端和服务器能够确认彼此是否具备通信能力。如果任何一方在握手过程中未能收到对方的响应,则连接无法建立,从而避免了无效的连接尝试。
- 防止重复连接:TCP协议通过三次握手来防止重复连接。在握手过程中,如果客户端或服务器收到了重复的SYN数据包,它们会丢弃这些数据包,从而避免了重复连接的产生。
- 确保数据传输的可靠性:通过三次握手,客户端和服务器能够建立起可靠的连接,确保后续数据的可靠传输。在数据传输过程中,TCP协议会采用各种机制(如超时重传、丢弃重复数据等)来应对异常情况,以保证连接的可靠性。
四、TCP三次握手的代码实现
下面是一个简单的TCP三次握手的代码示例,通过构建TCP报文来实现三次握手的过程。
代码语言:java复制public class TCPProtocolLayer implements IProtocol {
private static int HEADER_LENGTH = 20;
private int sequence_number = 2;
private int acknowledgement_number = 0;
private static int PSEUDO_HEADER_LENGTH = 12;
public static byte TCP_PROTOCOL_NUMBER = 6;
private static int POSITION_FOR_DATA_OFFSET = 12;
private static int POSITION_FOR_CHECKSUM = 16;
private static byte MAXIMUN_SEGMENT_SIZE_OPTION_LENGTH = 4;
private static byte MAXIMUN_SEGMENT_OPTION_KIND = 2;
private static byte WINDOW_SCALE_OPTION_KIND = 3;
private static byte WINDOW_SCALE_OPTION_LENGTH = 3;
private static byte WINDOW_SCALE_SHIFT_BYTES = 6;
private static byte TCP_URG_BIT = (1 << 5);
private static byte TCP_ACK_BIT = (1 << 4);
private static byte TCP_PSH_BIT = (1 << 3);
private static byte TCP_RST_BIT = (1 << 2);
private static byte TCP_SYN_BIT = (1 << 1);
private static byte TCP_FIN_BIT = 1;
@Override
public byte[] createHeader(HashMap<String, Object> headerInfo) {
short data_length = 0;
byte[] data = null;
if (headerInfo.get("data") != null) {
data = (byte[]) headerInfo.get("data");
}
byte[] header_buf = new byte[HEADER_LENGTH];
ByteBuffer byteBuffer = ByteBuffer.wrap(header_buf);
if (headerInfo.get("src_port") == null) {
return null;
}
short srcPort = (short) headerInfo.get("src_port");
byteBuffer.putShort(srcPort);
if (headerInfo.get("dest_port") == null) {
return null;
}
short destPort = (short) headerInfo.get("dest_port");
byteBuffer.putShort(destPort);
// 设置初始序列号
if (headerInfo.get("seq_num") != null) {
sequence_number = (int) headerInfo.get("seq_num");
}
// 设置确认号
if (headerInfo.get("ack_num") != null) {
acknowledgement_number = (int) headerInfo.get("ack_num");
}
byteBuffer.putInt(sequence_number);
byteBuffer.putInt(acknowledgement_number);
short control_bits = 0;
// 设置控制位
if (headerInfo.get("URG") != null) {
control_bits |= (1 << 5);
}
if (headerInfo.get("ACK") != null) {
control_bits |= (1 << 4);
}
if (headerInfo.get("PSH") != null) {
control_bits |= (1 << 3);
}
if (headerInfo.get("RST") != null) {
control_bits |= (1 << 2);
}
if (headerInfo.get("SYN") != null) {
control_bits |= (1 << 1);
}
if (headerInfo.get("FIN") != null) {
control_bits |= 1;
}
byteBuffer.putShort(control_bits);
System.out.println(Integer.toBinaryString(control_bits));
char window = 65535;
byteBuffer.putChar(window);
// 计算校验和(省略具体实现)
// short check_sum = 0;
// ...
return header_buf;
}
// 模拟三次握手过程(简化版)
public static void threeWayHandshake() {
TCPProtocolLayer tcpLayer = new TCPProtocolLayer();
// 第一次握手:客户端发送SYN数据包
HashMap<String, Object> synHeaderInfo = new HashMap<>();
synHeaderInfo.put("src_port", 12345);
synHeaderInfo.put("dest_port", 80);
synHeaderInfo.put("seq_num", 1);
synHeaderInfo.put("SYN", true);
byte[] synHeader = tcpLayer.createHeader(synHeaderInfo);
System.out.println("Client sends SYN packet:");
// 打印或处理SYN数据包(省略具体实现)
// 第二次握手:服务器发送SYN/ACK数据包
HashMap<String, Object> synAckHeaderInfo = new HashMap<>();
synAckHeaderInfo.put("src_port", 80);
synAckHeaderInfo.put("dest_