通过线性回归发送多个数据包来计算带宽

Calculating the bandwidth by sending several packets through linear regression

我实现了一个 TCP 客户端-服务器模型,通过发送不同大小的数据包数量来测试我与服务器的带宽,然后查看 RTT,然后通过线性回归计算带宽, 这是服务器代码:

 import java.io.*;
 import java.net.*;

 public class Server implements Runnable {

 ServerSocket welcomeSocket;
 String clientSentence;
 Thread thread;
 Socket connectionSocket;
 BufferedReader inFromClient;
 DataOutputStream outToClient;
 public Server() throws IOException {

     welcomeSocket = new ServerSocket(6588);
     connectionSocket = welcomeSocket.accept();

     inFromClient =  new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
     outToClient = new DataOutputStream(connectionSocket.getOutputStream());

     thread = new Thread(this);
     thread.start();
 }

@Override
public void run() {
    // TODO Auto-generated method stub

    while(true)
    {

    try {
        clientSentence = inFromClient.readLine();
        if (clientSentence != null) {
            System.out.println("Received: " + clientSentence);
            outToClient.writeBytes(clientSentence + '\n');
        }
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    }
}
public static void main(String[] args) throws IOException {
    new Server();
}

}

这是客户端中的方法 class return 每个数据包的 RTT 数组

    public int [] getResponseTime() throws UnknownHostException, IOException {
    timeArray = new int[sizes.length];
    for (int i = 0; i < sizes.length; i++) {
        sentence = StringUtils.leftPad("", sizes[i], '*');
        long start  = System.nanoTime();
        outToServer.writeBytes(sentence + '\n');
        modifiedSentence = inFromServer.readLine();
        long end = System.nanoTime();
        System.out.println("FROM SERVER: " + modifiedSentence);
        timeArray[i] = (int) (end - start);
        simpleReg.addData(timeArray[i]* Math.pow(10, -9), sizes[i] * 2); // each char is 2 bytes 
    }
    return timeArray;
}

当我得到斜率时,它 return 给我一个千字节的带宽,但是它们在同一个网络中,带宽应该更多。我做错了什么?

您必须使用线性回归还是可以使用不同的估计器?我实际上不确定线性回归是否是这里最好的方法。我很好奇,您是否碰巧知道任何建议在这种情况下使用它的消息来源?

请注意,尤其是初始 BW 测量值远小于实际最大吞吐量(由于 TCP 慢启动),因此使用将大的错误异常值考虑在内的度量估计非常重要。 在之前的工作中,我使用 谐波均值 来监控较长时间段内的带宽,效果非常好(在 links 上也有大带宽)。 调和平均值 相对于其他方法的优势在于,虽然它仍然很容易计算,但它减轻了大异常值的影响,这意味着估计值不容易被伪造。

给定一系列带宽测量 R_i,其中 i=0,1,2,..., n-1,调和平均值 计算如下: R_total = (n+1)/((n/R_total) + (1/R_n))

跳过前几个测量值(取决于您测量的频率...)也是一种很好的做法,例如 R_(0..5),因为您可能会由于初始准备而产生初始爆发不同的层,无论如何都处于缓慢启动阶段。

这里是 Java 中的示例实现。即使在这种情况下测量是通过文件下载完成的,它也可以很容易地应用于您的环境 - 只需使用您的回显服务器而不是文件下载:

public class Estimator
{
    private static double R; // harmonic mean of all bandwidth measurements
    private static int n = 0; // number of measurements
    private static int skips = 5; // skip measurements for first 5 socket.read() operations

    // size in bytes
    // start/end in ns
    public static double harmonicMean(long start, long end, double size){
        // check if we need to skip this initial value, since it might falsify our estimate
        if(skips-- > 0) return 0;

        // get current value of R
        double curR = (size/(1024*1024))/(double)((end - start)*Math.pow(10, -9));
        System.out.println(curR);
        if(n == 0) {
            // initial value
            R = curR;
        } else {
            // use harmonic mean
            R = (n+1)/((n/R)+(1/curR));
        }

        n++;

        return R;
    }

    public static void main(String[] args)
    {
        // temporary buffer to hold bytes
        byte[] buffer = new byte[1024*1024*10]; // 10MB buffer - just in case ...

        Socket socket = null;
        try {
            // measurement done through file download from server
            // prepare request
            socket = new Socket("yourserver.com",80);
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            InputStream is = socket.getInputStream();
            pw.println("GET /test_blob HTTP/1.1"); // a test file, e.g., 1MB big
            pw.println("Host: yourserver.com");
            pw.println("");
            pw.flush();

            // prepare measurement
            long start,end;
            double bytes = 0;
            double totalBytes = 0;
            start = System.nanoTime();
            while((bytes = is.read(buffer)) != -1) {
                // socket.read() occurred -> calculate harmonic mean
                end = System.nanoTime();
                totalBytes += bytes;
                harmonicMean(start, end, totalBytes);
            }

            // clean up
            is.close();
            pw.close();
        }
        catch(Exception e){
            e.printStackTrace();
        } 
        finally {
            if(socket != null) {
                try{
                    socket.close();
                } 
                catch(Exception e){
                    e.printStackTrace();
                }
            }
        }
        System.out.println(R+" MB/s");
    }
}

此外,为了完整起见,正如我在评论中提到的,测试 messages/files 足够大很重要,这样 TCP 才能达到 link 的全部吞吐量潜力。

另请注意,这是估计带宽的简化方法。在此示例中,我们从发送请求时开始测量(采用第一个时间戳),这意味着我们包括 link 传播和服务器处理延迟,这在 return 中将降低总体估计值。不管怎样,因为你好像用的是本地网络,我希望这些延迟的总和是相当小的,这意味着他们不会过多地伪造最终的估计。

我写了一篇关于 measuring TCP connection metrics inside an application layer 的小博客 post。那里对所有内容都有更详细的描述(尽管代码示例在 C 中)。