Java网络编程,使用UDP实现TCP(三), 基本实现四次挥手

发布时间:2023年12月17日

简介

四次挥手示意图

  • 在四次挥手过程中,第一次挥手中的Seq为本次挥手的ISN, ACK为 上一次挥手的 Seq+1,即最后一次数据传输的Seq+1。
  • 挥手信息由客户端首先发起。

实现步骤:

下面是TCP四次挥手的步骤:

  1. 第一次挥手(FIN):主动关闭方发送一个带有FIN(Finish)标志的TCP报文段给被动关闭方,表示主动关闭方已经没有数据要发送了。

  2. 第二次挥手(ACK):被动关闭方接收到第一次挥手的TCP报文段后,发送一个带有ACK(Acknowledgment)和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。

  3. 第三次挥手(FIN):被动关闭方发送一个带有FIN标志的TCP报文段给主动关闭方,表示被动关闭方也没有数据要发送了。

  4. 第四次挥手(ACK):主动关闭方接收到第三次挥手的TCP报文段后,发送一个带有ACK和确认序号的TCP报文段作为响应,表示已经收到了关闭请求。

在完成这四次挥手之后,TCP连接就正式关闭了。需要注意的是,每一次挥手都需要对方的确认才能进行下一步操作,这样可以确保双方都知道连接已经关闭,并且保证数据的完整性和可靠性。

这个过程可以简化为以下步骤:

  1. 主动关闭方发送FIN报文段。
  2. 被动关闭方接收到FIN后,发送ACK报文段作为确认。
  3. 被动关闭方发送FIN报文段。
  4. 主动关闭方接收到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,有了更深入的了解。
文章来源:https://blog.csdn.net/weixin_74783792/article/details/134960039
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。