3

ループ内でsend/関数を絞り込むには、どのような方法がありますか。sendto()ネットワーク用のポート スキャナーを作成していて、2 つの方法を試しましたが、ローカルでしか機能しないようです (自宅のマシンでテストすると機能しますが、別のマシンでテストしようとすると、適切なポート スキャナーを作成したくありません)。スロットル)。

方法 1

/proc/net/dev私はもともと「送信されたバイト数」属性を解析して読み取り、それに基づいて睡眠時間を計算していました。それはローカルで機能しました(スリープ遅延は帯域幅の流れを調整するために調整されていました)が、別のサーバーでも試してみるとすぐに、/proc/net/devデータが正しく調整されていないようでした. ローカルでスキャンしているマシンで実行dstatしたところ、多くのデータが高速に出力されていました。

方法 2

次に、送信した合計バイト数を追跡​​しtotal_sent、帯域幅スレッドが読み取ってスリープタイマーを計算する変数に追加しようとしました。これはローカルマシンでも機能しましたが、サーバーで試してみると、帯域幅スレッドがスリープを0に減らすように帯域幅スレッドがチェックするたびに1〜2個のパケットしか送信されていないとのことでしたがtotal_sent、0でもtotal_sent変数は機能しました睡眠時間が減ったため増加しませんでしたが、代わりに同じままでした。

全体として、Linux コンピューターの帯域幅を監視し、各/ソケット呼び出しusleep()の前後に渡して帯域幅を調整するスリープ時間を計算する方法が必要です。sendsendto()

編集:私が言及するのを忘れていた他のいくつかのことは、マシンのアップロード速度を計算するスピードテスト機能があり、2 つのスレッドがあることです。1 つのスレッドは帯域幅の使用状況に基づいてグローバル スリープ タイマーを調整し、スレッド 2 はパケットをリモート マシンのポートに送信して、ポートが開いているかどうかをテストし、フィンガープリントを作成します (現在、sendto()これをすべてテストするために で udp パケットを使用しています)。 .

sendを使用して/sendto()呼び出しの帯域幅スロットリングを実装するにはどうすればよいですか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++;
  }

}

私の送信スレッドは、テスト用に udp パケットを送信するループ内の単純なsendto()ルーチンです。は my 、は「A」で埋められた 64 バイトの文字配列であり、isです。while(1)socksockfdbuffsinmy sockaddr_in

  while(1)
  {
    TOTAL_BYTES_SEND += 64;

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


    usleep(SLEEP);
  }

dstatローカル マシンとリモート マシンでの使用状況を確認できるため、ソケット関数が機能することはわかっています。この帯域幅コードはローカル システムで機能します (すべての変数が必要に応じて変更されます) が、経過バイト数でテストしようとしたサーバーでは変更されず (スレッドの反復ごとに常に 64/128 です)、SLEEP0 までスロットリングされます。理論的には、マシンがパケットをより速く送信できるようにする必要がありますが、SLEEP経過バイト数が 0 に等しくても 64/128 のままです。sendto()-1 を返す関数をチェックする if ステートメント内に関数をエンコードしprintf、エラー コードを -ing して警告しましたが、私が行ったテストにはありませんでした。

4

2 に答える 2

4

これは、送信スレッドでスロットルのスリープ時間を計算することで最も直接的に解決できるようです。この作業を行うために別のスレッドが役立つかどうかはわかりません。

これを行う 1 つの方法を次に示します。

送信レートを測定する時間枠を選択します。ターゲット帯域幅に基づいて、これにより、その時間の最大バイト数が得られます。次に、各 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);
      }
    }
  }
}
于 2012-11-26T05:38:40.630 に答える
1

これをアプリケーション レベルで行いたい場合は、trickle などのユーティリティを使用して、アプリケーションで使用できるソケット転送速度を制限または調整できます。

例えば、

trickle -s -d 50 -w 100 firefox

firefox50KB/秒の最大ダウンロード レートと 100KB のピーク検出ウィンドウで開始します。これらの値を変更すると、アプリケーションのテストに適した値が生成される場合があります。

于 2012-11-26T14:52:01.750 に答える