您当前的位置: 首页 > 

恐龙弟旺仔

暂无认证

  • 0浏览

    0关注

    282博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

TCP协议基于计时器的重传策略模拟实战

恐龙弟旺仔 发布时间:2022-06-03 21:01:36 ,浏览量:0

前言:

关于TCP协议的重传策略,是TCP数据传输正确性的重要保证。

由于下层网络层协议可能出现的包丢失、重复、失序包等问题,当TCP协议基于某种策略确认当前包已经发生以上情况,就会启动重传。

TCP拥有两套机制来完成重传:基于超时时间;基于确认消息(SACK);

本文主要来模拟下基于超时时间的重传。

1.环境准备

笔者准备两台机器,一台启动ServerSocket服务,另一台就启动telnet命令进行连接发送请求等操作

1.1 ServerSocket

笔者这里使用的是标准的java ServerSocket,来启动一个端口监听,代码如下:

 

public class BIOServerSocket {

    private String address;
    private int port;

    public BIOServerSocket(String address, int port) {
        this.address = address;
        this.port = port;
    }

    public void startServer() {
        try {
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(address, port));
            System.out.println("bio server start...");

            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("client connect...");

                // 写入 thread
                ServerWriteThread serverWriteThread = new ServerWriteThread(clientSocket);
                serverWriteThread.start();

                // 读取数据
                read(clientSocket);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void read(Socket clientSocket) {
        try {
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            String msg = "";
            while ((msg = bufferedReader.readLine()) != null) {
                System.out.println("receive msg: " + msg);
            }
        } catch (IOException e) {
            try {
                clientSocket.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        String address = "192.168.3.8";
        int port = 9999;

        BIOServerSocket bioServerSocket = new BIOServerSocket(address, port);
        bioServerSocket.startServer();
    }
}

/**
 * 从Scanner获取输入信息,并写回到client
 */
class ServerWriteThread extends Thread {
    private Socket socket;
    private PrintWriter writer;
    private Scanner scanner;

    public ServerWriteThread(Socket socket) throws IOException{
        this.socket = socket;
        scanner = new Scanner(System.in);
        this.writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
    }

    @Override
    public void run() {
        String msg = "";
        try {
            while ((msg = scanner.nextLine()) != null) {
                if (msg.equals("bye")) {
                    socket.close();
                    break;
                }
                writer.println(msg);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码很简单,笔者不再多述。

1.2 启动客户端telnet

笔者在另一台机器上(Ubuntu docker)启动一个telnet命令,用来创建对上述ServerSocket的连接

root@93de58bae514:/# telnet 192.168.3.8 9999
Trying 192.168.3.8...
Connected to 192.168.3.8.
Escape character is '^]'.
1.3 wireshark监听

笔者在这里提前启动wireshark监听服务,当1.2步骤中的telnet执行完成后,我们可以看到三次握手建立完成。如下所示:

2.超时重传模拟

这里的超时重传,我们本质上是在模拟当客户端发送数据包后,在规定时间内没有收到服务端的ACK包时,自发的一种重传当前数据包策略。

我们通过客户端来模拟下该动作

2.1 正常收发包

客户端正常发包

 

root@93de58bae514:/# telnet 192.168.3.8 9999
Trying 192.168.3.8...
Connected to 192.168.3.8.
Escape character is '^]'.

# 以下数据包逐个发送出去
a
b
c

服务端正常收到以上数据包

client connect...
receive msg: a
receive msg: b
receive msg: c
2.2 模拟网络异常后的发包

如果模拟两台机器之间的网络异常呢?

笔者就直接把服务端的网络禁止掉(服务端笔者使用的是Windows电脑,直接设置成飞行模式,这样外部请求就进不来),此时服务端程序还在,但是无法收发数据包

此时客户端再次发送数据包

root@93de58bae514:/# telnet 192.168.3.8 9999
Trying 192.168.3.8...
Connected to 192.168.3.8.
Escape character is '^]'.

# 以下数据包逐个发送出去
a
b
c

# 网络异常后再次发送数据包
d
Connection closed by foreign host.

这时我们再看服务端,没有出现receive msg: d,说明客户端的数据包确实没有发送到服务端。

那么数据在哪呢?我们还是通过wireshark来看下包的发送情况:

 

在163行数据第一次发送数据d失败后,后续一直在重试,一直重试到336行数据为止。

2.3 TCP超时重传规律

根据wireshark的包发送情况,我们可以总结出来这么些规律

1.与SYN包重传策略类似,数据包的重传基本也是指数级避退的(看第二列数据[发送时间]可以看出这个规律)

2.数据包的重传有个最大限制,上图中重传了15次,最终直接关闭了连接

2.4 TCP超时重传阈值设置

TCP拥有两个阈值来决定如何重传同一个报文段。

R1表示TCP再向IP层传递消极建议前,愿意尝试重传的次数;

R2表示指示TCP应放弃当前连接的时机

可以通过查看系统参数获取:

root@93de58bae514:/# sysctl -a | grep net.ipv4.tcp_retries
sysctl: reading key "net.ipv6.conf.all.stable_secret"
net.ipv4.tcp_retries1 = 3  # R1
net.ipv4.tcp_retries2 = 15 # R2

基于R2而言,比较符合我们上述测试的结果,在重试了15次之后,终于放弃重试,关闭当前连接。

总结:

笔者在模拟这个重传还是比较困难的,一直想不到好的办法来模拟,直接关闭服务端进程的话,同时所有的客户端连接也被直接关闭了。

后来才想明白,直接关闭网络,太为难了。

TCP超时重传策略看了N多遍,但总是忘记,所以最好的学习的办法还是实战。实战一次之后就特别清晰了。

与君共勉之!

关注
打赏
1655041699
查看更多评论
立即登录/注册

微信扫码登录

0.0372s