简介
四次挥手示意图
- 在四次挥手过程中,第一次挥手中的Seq为本次挥手的ISN, ACK为 上一次挥手的 Seq+1,即最后一次数据传输的Seq+1。
- 挥手信息由客户端首先发起。
实现步骤:
下面是TCP四次挥手的步骤:
-
第一次挥手(FIN):主动关闭方发送一个带有FIN(Finish)标志的TCP报文段给被动关闭方,表示主动关闭方已经没有数据要发送了。
-
第二次挥手(ACK):被动关闭方接收到第一次挥手的TCP报文段后,发送一个带有ACK(Acknowledgment)和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。
-
第三次挥手(FIN):被动关闭方发送一个带有FIN标志的TCP报文段给主动关闭方,表示被动关闭方也没有数据要发送了。
-
第四次挥手(ACK):主动关闭方接收到第三次挥手的TCP报文段后,发送一个带有ACK和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。
在完成这四次挥手之后,TCP连接就正式关闭了。需要注意的是,每一次挥手都需要对方的确认才能进行下一步操作,这样可以确保双方都知道连接已经关闭,并且保证数据的完整性和可靠性。
这个过程可以简化为以下步骤:
- 主动关闭方发送FIN报文段。
- 被动关闭方接收到FIN后,发送ACK报文段作为确认。
- 被动关闭方发送FIN报文段。
- 主动关闭方接收到FIN后,发送ACK报文段作为确认。
这样,TCP连接就完成了关闭过程。
修改说明:
我将客户端的发送消息和服务端的接收消息做了一些简单的封装:
客户端:
public void sendMsg(String dataMsg, DatagramSocket datagramSocket) throws IOException {
byte[] bytes = dataMsg.getBytes();
DatagramPacket datagramPacketMsg = new DatagramPacket(bytes, 0,bytes.length, new InetSocketAddress("localhost",9999));
datagramSocket.send(datagramPacketMsg);
}
服务端:
//接收数据
public String receive(DatagramSocket datagramSocket, int time1, int time2) throws IOException {
//设置超时时间
datagramSocket.setSoTimeout(time1);
//创建数据包,用于接收数据
byte[] bytes3 = new byte[1024];
DatagramPacket datagramPacket3 = new DatagramPacket(bytes3, bytes3.length);
datagramSocket.receive(datagramPacket3);
//停止计时器
datagramSocket.setSoTimeout(time2);
String s3 = new String(datagramPacket3.getData(), 0, datagramPacket3.getLength());
return s3;
}
第一次挥手
客户端发送关闭请求:
//四次挥手,关闭连接
System.out.println("====================");
System.out.println("四次挥手:");
System.out.println("第一次挥手: 客户端 -> 服务端");
System.out.println("数据发送...");
connectionMarks.setFinMark(2);
String finMark = String.valueOf(connectionMarks.getFinMark());
connectionMarks.setACKMark(1);
String ACKFin = String.valueOf(connectionMarks.getACKMark());
String SeqFin = String.valueOf(connectionMarks.getSeq());
String ACKS1 = String.valueOf(Integer.parseInt(SeqD1) + 1);
String dataF1 = finMark + "/" + ACKFin + " " + SeqFin + " " + ACKS1;
clientMsg.sendMsg(dataF1, datagramSocket);
服务端接收数据:
//四次握手
//第一次
System.out.println("====================");
String receiveB1 = serverMsg.receive(datagramSocket, 0, 0);
System.out.println("接收到的数据段为:" + receiveB1);
String[] s1 = receiveB1.split(" ");
String[] splitS1 = s1[0].split("/");
if (
!(splitS1[0].equals("2")
|| splitS1[1].equals("1")
|| s1[2].equals(String.valueOf(Integer.parseInt(SeqD1) + 1)))
){
throw new WrongConnectionException("非本次连接");
}
第二次挥手
服务端发送第一次挥手的ACK
//第二次
System.out.println("====================");
System.out.println("服务端 -> 客户端");
System.out.println("数据发送...");
String SeqB2 = s1[2];
String ACKB2 = String.valueOf(Integer.parseInt(s1[1]) + 1);
connectionMarks.setACKMark(1);
String ackMarkB = String.valueOf(connectionMarks.getACKMark());
String dataMsgB2 = ackMarkB+ " " + SeqB2 + " " + ACKB2;
byte[] datasB2 = dataMsgB2.getBytes();
DatagramPacket datagramPacketB2 = new DatagramPacket(datasB2, 0,datasB2.length, new InetSocketAddress("localhost",8888));
//调用对象发送数据
datagramSocket.send(datagramPacketB2);
客户端接收
System.out.println("====================");
System.out.println("开始接收数据段...");
byte[] bytesB2 = new byte[1024];
DatagramPacket datagramPacketB2 = new DatagramPacket(bytesB2, bytesB2.length);
datagramSocket.receive(datagramPacketB2);
String receiveMsgB2 = new String(datagramPacketB2.getData(), 0, datagramPacketB2.getLength());
System.out.println("接收到的数据段为:" + receiveMsgB2);
第三次挥手
服务端发送请求关闭给客户端
System.out.println("====================");
System.out.println("服务端 -> 客户端");
System.out.println("数据发送...");
String SeqB3 = SeqB2;
String FinMark = splitS1[0];
String ACKB3 = ACKB2;
String dataMsgB3 = FinMark + "/" + ackMarkB+ " " + SeqB3 + " " + ACKB3;
byte[] datasB3 = dataMsgB3.getBytes();
DatagramPacket datagramPacketB3 = new DatagramPacket(datasB3, 0,datasB3.length, new InetSocketAddress("localhost",8888));
//调用对象发送数据
datagramSocket.send(datagramPacketB3);
客户端接收数据,需要校验,如果收到为关闭请求,则发送ACK给服务端
System.out.println("====================");
System.out.println("开始接收数据段...");
byte[] bytesB3 = new byte[1024];
DatagramPacket datagramPacketB3 = new DatagramPacket(bytesB3, bytesB3.length);
datagramSocket.receive(datagramPacketB3);
String receiveMsgB3 = new String(datagramPacketB3.getData(), 0, datagramPacketB3.getLength());
System.out.println("接收到的数据段为:" + receiveMsgB3);
String[] splitB3 = receiveMsgB3.split(" ");
String[] split2 = splitB3[0].split("/");
if (
!(split2[0].equals("2")
|| split2[1].equals("1")
||splitB3[1].equals(ACKS1)
||splitB3[2].equals(String.valueOf(Integer.parseInt(SeqFin) + 1)))
){
throw new WrongConnectionException("非本次连接");
}
第四次挥手
客户端接收并发送第三次挥手的ACK给服务端
System.out.println("====================");
System.out.println("第四次挥手: 客户端 -> 服务端");
System.out.println("数据发送...");
String ackMark4 = ACKFin;
String SeqB4 = SeqFin;
String ACKB4 = String.valueOf(Integer.parseInt(ACKS1) + 1);
String dataB4 = ackMark4 + " " + SeqB4 + " " + ACKB4;
clientMsg.sendMsg(dataB4, datagramSocket);
//关闭流
datagramSocket.close();
客户端接收到ACK并且关闭
System.out.println("====================");
String receiveB4 = serverMsg.receive(datagramSocket, 0, 0);
System.out.println("接收到的数据段为:" + receiveB4);
//关闭流
datagramSocket.close();
完成总结:
- 基本完成了UDP实现TCP,但仍有欠缺。
- 实现过程中代码的复用过高,没有进行有效的方法封装。
- 代码不够成熟,还由一些不完善的地方,没有实现MTU机制。
- 对UDP和TCP,有了更深入的了解。