通过线性回归发送多个数据包来计算带宽
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 中)。
我实现了一个 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 中)。