C Linux Bandwidth Throttling of Application

↘锁芯ラ 提交于 2019-12-01 09:50:37

It seems like this could be most directly solved by calculating the throttle sleep time in the send thread. I'm not sure I see the benefit of another thread to do this work.

Here is one way to do this:

Select a time window in which you will measure your send rate. Based on your target bandwidth this will give you a byte maximum for that amount of time. You can then check to see if you have sent that many bytes after each sendto(). If you do exceed the byte threshold then sleep until the end of the window in order to perform the throttling.

Here is some untested code showing the idea. Sorry that clock_gettime and struct timespec add some complexity. Google has some nice code snippets for doing more complete comparisons, addition, and subtraction with 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);
      }
    }
  }
}

If you'd like to do this at the application level, you could use a utility such as trickle to limit or shape the socket transfer rates available to the application.

For instance,

trickle -s -d 50 -w 100 firefox

would start firefox with a max download rate of 50KB/s and a peak detection window of 100KB. Changing these values may produce something suitable for your application testing.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!