TCP的拥塞

先看一个演示
服务端代码:

package io.unittest;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;

/**
 * Author: ljf
 * CreatedAt: 2021/3/28 上午10:05
 */
public class SocketIOProperties {
    
    

    private static final int RECEIVE_BUFFER = 10;
    private static final int SO_TIMEOUT = 0;
    private static final boolean REUSE_ADDR = false;
    private static final int BACK_LOG = 2; // 连接的等待队列满了之后还能拍几个,linux一切皆文件嘛,意味着等着不分配文件描述符的有两个
    private static final boolean CLI_KEEPALIVE = false;
    private static final boolean CLI_OOB = false;
    private static final int CLi_REC_BUF = 20;
    private static final boolean CLI_REUSE_ADDR = false;
    private static final int CLI_SEND_BUF = 20;
    private static final boolean CLI_LINGER = true; // 服务关闭后端口是否立即释放
    private static final int CLI_LINGER_N = 0;
    private static final int CLI_TIMEOUT = 0;
    private static final boolean CLI_NO_DELAY = false;

    public static void main(String[] args) {
    
    
        ServerSocket server = null;
        try {
    
    
            server = new ServerSocket();
            server.bind(new InetSocketAddress(9090), BACK_LOG);
            server.setReceiveBufferSize(RECEIVE_BUFFER);
            server.setReuseAddress(REUSE_ADDR);
            server.setSoTimeout(SO_TIMEOUT);
        } catch (SocketException e) {
    
    
            e.printStackTrace();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("server up use 9090 !");

        try {
    
    
            while (true) {
    
    
                System.in.read(); // 这里阻塞住,看没有客户端的情况下,server端怎么处理连接

                Socket client = server.accept();

                System.out.println("client port: " + client.getPort());

                client.setKeepAlive(CLI_KEEPALIVE);
                client.setOOBInline(CLI_OOB);
                client.setReceiveBufferSize(CLi_REC_BUF);
                client.setReuseAddress(CLI_REUSE_ADDR);
                client.setSendBufferSize(CLI_SEND_BUF);
                client.setSoLinger(CLI_LINGER, CLI_LINGER_N);
                client.setSoTimeout(CLI_TIMEOUT);
                client.setTcpNoDelay(CLI_NO_DELAY);

                // client.read //阻塞 没有 -1 0
                new Thread(
                        () -> {
    
    
                            try {
    
    
                                InputStream in = client.getInputStream();
                                BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                                char[] data = new char[1024];
                                while (true) {
    
    
                                    int num = reader.read();

                                    if (num > 0) {
    
    
                                        System.out.println("client read some data is : " + num + " val :" + new String(data, 0, num));
                                    } else if (num == 0) {
    
    
                                        System.out.println("client readed nothing!");
                                        continue;
                                    } else {
    
    
                                        System.out.println("client readed -1 ..."); // 客户端断开,就是-1
                                        System.in.read();
                                        break;
                                    }
                                }
                            } catch (IOException e) {
    
    
                                e.printStackTrace();
                            }
                        }
                ).start();
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            try {
    
    
                server.close();
            } catch (IOException e) {
    
    
                e.printStackTrace();
            }
        }
    }
}

有System.in.read(),阻塞
启动服务端程序,阻塞主,没有应用程序接收,这时候缓冲区大小是0,
在这里插入图片描述
这时候启动一个客户端,模拟客户端发送数据,这时候缓冲区开始涨了
在这里插入图片描述
不够,继续发到缓冲区撑爆,比如我发到1152就不涨了
在这里插入图片描述
那么既然不涨了,多发的数据就被丢弃了,那是丢掉了头部的还是尾部的呢,这时候服务端回车,跳过阻塞,接收数据,发现是尾部的被丢掉了。即tcp的拥塞。

tcp怎么解决拥塞

窗口机制,还是上边那个服务器阻塞的模型,看图

在这里插入图片描述
客户端连接的时候告诉,我有多少个窗口 win,数据包是多大 mss(1460,是ifconfig里mtu的1500减去ip和端口各占的20个字节) ,然后服务端告诉客户端自己的win和mss,然后客户端确认,然后连接建立,就通信。
在每次的通信数据交互过程中,客户端和和服务端都告诉自己的窗口还有多少,如果服务端的窗口不够用了,就阻塞住,直到数据处理完(交给应用程序进行下一步的处理),有空闲的窗口了就给客户端发一个ack包通知队列有剩余了,客户端才继续发包给服务端。反过来客户端同理。就保证了tcp的包不能丢失。即拥塞控制

猜你喜欢

转载自blog.csdn.net/weixin_39370859/article/details/115284992