C Linux应用程序的带宽限制

时间:2012-11-26 02:27:15

标签: c linux throttling

有什么方法可以尝试在循环中限制send / sendto()函数。我正在为我的网络创建一个端口扫描程序,我尝试了两种方法,但它们似乎只在本地工作(当我在家用机器上测试它们时它们工作但是当我尝试在另一台机器上测试它们时它不想创建适当的节流)。

方法1

我最初解析/proc/net/dev并读取“bytes sent”属性并将我的睡眠时间基于此。这在本地工作(睡眠延迟正在调整以调整带宽流量)但是当我在另一台服务器上尝试使用/proc/net/dev时,它似乎没有正确调整数据。我在本地扫描的机器上运行dstat,并且输出的数据很快。

方法2

然后我尝试跟踪我发送的总字节数,并将其添加到我的带宽线程将读取的total_sent变量中,并为其计算睡眠定时器。这也适用于我的本地机器,但当我在服务器上尝试它时,它说它每次我的带宽线程检查total_sent时只发送1-2个数据包使我的带宽线程将睡眠减少到0,但是在{0} total_sent变量由于睡眠时间缩短而没有增加,而是保持不变。

总的来说,我想要一种方法来监控Linux计算机的带宽并计算一个睡眠时间,我可以在每次usleep() / send套接字调用之前或之后传入sendto()节流带宽。

编辑:其他一些我忘记提及的事情是我有一个最快的功能来计算机器的上传速度,我有2个线程。 1个线程根据带宽使用情况调整全局睡眠定时器,线程2将数据包发送到远程机器上的端口以测试它们是否打开并对其进行指纹化(现在我只使用带有sendto()的udp数据包测试这一切。

如何使用sendsendto() / usleep()来电实施带宽限制。

编辑:以下是我的带宽监控线程的代码。不要关心结构的东西,它只是我将数据传递给线程的方式。

void *bandwidthmonitor_cmd(void *param)
{
  int i = 0;
  double prevbytes = 0, elapsedbytes = 0, byteusage = 0, maxthrottle = 0;

  //recreating my param struct i passed to the thread
  command_struct bandwidth = *((command_struct *)param);
  free(param);

  //set SLEEP (global variable) to a base time in case it was edited and not reset
  SLEEP = 5000;

  //find the maximum throttle speed in kb/s (takes the global var UPLOAD_SPEED
  //which is in kb/s and times it by how much bandwidth % you want to use
  //and devides by 100 to find the maximum in kb/s
  //ex: UPLOAD_SPEED = 60, throttle = 90, maxthrottle = 54
  maxthrottle = (UPLOAD_SPEED * bandwidth.throttle) / 100;
  printf("max throttle: %.1f\n", maxthrottle);

  while(1)
  {
      //find out how many bytes elapsed since last polling of the thread
      elapsedbytes = TOTAL_BYTES_SEND - prevbytes;
      printf("elapsedbytes: %.1f\n", elapsedbytes);

      //set prevbytes to our current bytes so we can have results next loop
      prevbytes = TOTAL_BYTES_SEND;

      //convert our bytes to kb/s
      byteusage = 8 * (elapsedbytes / 1024);

      //throttle control to make it adjust sleep 20 times every 30~ 
      //iterations of the loop
      if(i & 0x40)
      {
           //adjust SLEEP by 1.1 gain
           SLEEP += (maxthrottle - byteusage) * -1.1;//;


           if(SLEEP < 0){
               SLEEP = 0;
           }
           printf("sleep:%.1f\n\n", SLEEP);
      }

      //sleep the thread for a short bit then start the process over
      usleep(25000);
      //increment variable i for our iteration throttling
      i++;
  }

}

我的发送线程只是sendto()循环中的一个简单while(1)例程,用于发送udp数据包进行测试。 socksockfdbuff是64字节字符数组,填充“A”,sinmy sockaddr_in

  while(1)
  {
    TOTAL_BYTES_SEND += 64;

    sendto(sock, buff, strlen(buff), 0, (struct sockaddr *) &sin, sizeof(sin))


    usleep(SLEEP);
  }

我知道我的套接字功能有效,因为我可以在本地计算机和远程计算机上查看dstat中的用法。这个带宽代码可以在我的本地系统上运行(所有变量都按照它们应该更改)但是在服务器上我尝试对经过的字节进行测试不会改变(每次迭代的线程总是64/128)并导致SLEEP限制为0,这理论上应该使机器更快地发送数据包,但即使SLEEP等于0经过的字节仍然是64/128。我还在if语句中编写了sendto()函数,检查返回-1的函数并通过printf警告我 - 错误代码但是测试中没有一个我已经完成。

2 个答案:

答案 0 :(得分:4)

看起来这可以通过计算发送线程中的节流睡眠时间来最直接地解决。我不确定我是否看到另一个线程的好处来完成这项工作。

以下是一种方法:

选择一个时间窗口,您可以在其中测量发送速率。根据您的目标带宽,这将为您提供该时间段的最大字节数。然后,您可以检查每个sendto()之后是否发送了那么多字节。如果超过字节阈值,则休眠直到窗口结束,以便执行限制。

这是一些未经测试的代码,显示了这个想法。抱歉,clock_gettime和struct timespec增加了一些复杂性。 Google提供了一些很好的代码片段,可以使用struct timespec进行更完整的比较,添加和减法。

#define MAX_BYTES_PER_SECOND (128L * 1024L)
#define TIME_WINDOW_MS 50L
#define MAX_BYTES_PER_WINDOW ((MAX_BYTES_PER_SECOND * TIME_WINDOW_MS) / 1000L)

#include <time.h>
#include <stdlib.h>

int foo(void) {
  struct timespec window_start_time;

  size_t bytes_sent_in_window = 0;
  clock_gettime(CLOCK_REALTIME, &window_start_time);

  while (1) {
    size_t bytes_sent = sendto(sock, buff, strlen(buff), 0, (struct sockaddr *) &sin, sizeof(sin));
    if (bytes_sent < 0) {
      // error handling
    } else {
      bytes_sent_in_window += bytes_sent;

      if (bytes_sent_in_window >= MAX_BYTES_PER_WINDOW) {
        struct timespec now;
        struct timespec thresh;

        // Calculate the end of the window
        thresh.tv_sec = window_start_time.tv_sec;
        thresh.tv_nsec = window_start_time.tv_nsec;
        thresh.tv_nsec += TIME_WINDOW_MS * 1000000;
        if (thresh.tv_nsec > 1000000000L) {
          thresh.tv_sec += 1;
          thresh.tv_nsec -= 1000000000L;
        }

        // get the current time
        clock_gettime(CLOCK_REALTIME, &now);

        // if we have not gotten to the end of the window yet
        if (now.tv_sec < thresh.tv_sec ||
            (now.tv_sec == thresh.tv_sec && now.tv_nsec < thresh.tv_nsec)) {

          struct timespec remaining;

          // calculate the time remaining in the window
          //  - See google for more complete timespec subtract algorithm
          remaining.tv_sec = thresh.tv_sec - now.tv_sec;
          if (thresh.tv_nsec >= now.tv_nsec) {
            remaining.tv_nsec = thresh.tv_nsec - now.tv_nsec;
          } else {
            remaining.tv_nsec = 1000000000L + thresh.tv_nsec - now.tv_nsec;
            remaining.tv_sec -= 1;
          }

          // Sleep to end of window
          nanosleep(&remaining, NULL);
        }

        // Reset counters and timestamp for next window
        bytes_sent_in_window = 0;
        clock_gettime(CLOCK_REALTIME, &window_start_time);
      }
    }
  }
}

答案 1 :(得分:1)

如果您想在应用程序级别执行此操作,可以使用trickle等实用程序来限制或调整应用程序可用的套接字传输速率。

例如,

trickle -s -d 50 -w 100 firefox

将启动firefox,最大下载速率为50KB / s,峰值检测窗口为100KB。更改这些值可能会产生适合您的应用程序测试的内容。