こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

fgetsについて

こんにちは。こちらのカテゴリには初めての投稿です。よろしくお願いします。

C++を用いて、Fedora上で"tcp dump"コマンドを実行し、1秒ごとのeth0(NIC)を通過したackパケットを記録するプログラムを作成しています。(150秒分記録)
ソースコードは質問文最後に記載します。

質問は、下記のプログラムでtcpdumpから1行分実行結果を取り出す際に使用しているfgets関数についてです。
調べてみると、fgtes関数は失敗した際は、NULLを返し「停止」するとあります。
http://www.nxmnpg.com/ja/3/fgets
これがおそらく原因かと思いますが、何も通信が行われていない状況で実行すると、
temp = fgets(buf,150,pp);
の行でプログラムが止まってしまいます。(何かしら通信がある(パケットが通る)と再開します)
よって、正確な時間計測ができず、困っています。

私はC++を勉強したてで、あまり読みやすいプログラムではないと思います。申し訳ありません。
fgetsの代わりになるような関数、または方法はありませんでしょうか。
ご回答、よろしくお願い致します。


//num_ack[]に各秒におけるパケット数を記録します。
//教えてgooの仕様上、tabや半角スペースが使えないので行頭のスペースはすべて全角です。ご了承ください。


#include <iostream>
#include <stdlib.h>
#include <time.h>
#include <string.h>
using namespace std;

#define N 150
int main()
{
   time_t start,end;

   int num_ack[N];
   int i=0;

   char buf[150];
   char *src,*temp,*srcstr;
   char cmp[150];
   srcstr = "ack";//検索ワード:"ack"

   FILE *pp;

   pp = popen("tcpdump -i eth0","r");

   //popen失敗検知
   if(pp == NULL)
   {
      cout << "tcpdump failed" << endl;
      exit(EXIT_FAILURE);
   }

   for(int k=0;k<N;k++) //num_ackを初期化
   {
      num_ack[k] = 0;
   }


   while(1)
   {
      start = time(NULL);
      end = time(NULL);

      while((int)(end - start) <1) //1秒間計測
      {
         temp = fgets(buf,150,pp); //tcpdumpから1行(1パケット分)をbufへ取得
         src = strstr(buf,srcstr);//bufから、"ack"を検索

         if(src != NULL && strcmp(buf,cmp) !=0) //検索結果がNULLでなく、前回から変更がある場合
         {
            num_ack[i] += 1;
            strcpy(cmp,buf);
         }
         end = time(NULL);
      }
      j += 1;
   }


   pclose(pp);

   return num_ack;
}

投稿日時 - 2013-01-18 16:58:13

QNo.7898096

困ってます

質問者が選んだベストアンサー

>これがおそらく原因かと思いますが、何も通信が行われていない状況で実行すると、
>temp = fgets(buf,150,pp);
>の行でプログラムが止まってしまいます。(何かしら通信がある(パケットが通る)と再開します)

tcpdumpが何か出力するのを待ってる状態なので止まってるように見えるのは当然です。
例えば
#include <stdio.h>

int
main()
{
char buf[128];

if (fgets(buf, sizeof(buf), stdin) != NULL) {
printf("%s\n", buf);
}
return 0;
}

をコンパイルし実行するとどうなるかわかります?
popenとfgetsでやろうとするならsetbufでバッファリングをオフにしてselectを使って入力があるかどうかを調べるか、fcntlでノンブロッキングモードにしておくかじゃないかな

投稿日時 - 2013-01-19 00:27:45

お礼

ご回答、ありがとうございます。
例に挙げられているソースをコンパイル、実行するとstdinがあるまで待ち、入ればそれを表示する動作のようです。
この例から、fgetsがtcpdumpが何か出力するのを待つ、という状況が把握できました。
fcntlを用いてノンブロッキングすることで問題は解決しました!

投稿日時 - 2013-01-19 23:33:39

このQ&Aは役に立ちましたか?

0人が「このQ&Aが役に立った」と投票しています

回答(5)

ANo.5

横道に完全にそれた感じもしつつ……。

http://www.nxmnpg.com/ja/3/fgets
> 読込みは、改行文字が見つかったり、ファイルの終了 あるいはエラーが見つかったりした場合に停止します。
の文の構造は,
「読込みは」(条件)「停止する」
ですね。つまり,停止するのは「読込み」です。

英語版では,
http://www.nxmnpg.com/3/fgets
> Reading stops when a newline character is found, at end-of-file or error.
と,Readingとstopsが並んでいて分かり易いのですが。

投稿日時 - 2013-01-19 02:56:15

お礼

ご回答ありがとうございます。
文構造がうまく読めていなかったですね。。。
今回の件でfgetsの動作について少し理解できました!
ありがとうございました。

投稿日時 - 2013-01-19 23:34:56

ANo.4

#2へ
困ったことに
http://www.nxmnpg.com/ja/3/fgets
には本当に「停止する」と書いてあります・・

投稿日時 - 2013-01-19 00:35:23

ANo.2

「fgtes関数は失敗した際は、NULLを返し「停止」するとあります」というのが何をどう読んだ結果の理解なのかさっぱりわからん.... 「停止」ってなんだ.

wait系でがんばるのかなぁ. それでも本当に「1秒ごと」とはならないかもしれんけど.

投稿日時 - 2013-01-19 00:06:53

お礼

ご回答ありがとうございます。
fgets関数の動作については質問文のURLの文からです。
No.5様ご指摘のように、停止するのは"読み込み"のようでした。

投稿日時 - 2013-01-19 23:30:30

ANo.1

>   while(1)

このループを抜ける条件は何ですか?ずっと回りっぱなしのように見えます。

>調べてみると、fgtes関数は失敗した際は、NULLを返し「停止」するとあります。

ということでしたら、

>         temp = fgets(buf,150,pp); //tcpdumpから1行(1パケット分)をbufへ取得

tempがNULLかどうかを判定する必要はありませんか?

>         if(src != NULL && strcmp(buf,cmp) !=0)

このif文を初めて通るときのcmpの値はどうなっていると思いますか?

>      j += 1;

変数 j の定義が見当たりません。i が正しいですか?

投稿日時 - 2013-01-18 21:57:56

補足

ご指摘・ご回答ありがとうございます。
whileの条件と、j += 1について訂正したものを、while文の中身のみ、再掲いたします。

fgetsが停止するというのは、仮にfgetsの結果がNULLだと、そこでプログラムの実行が止まってしまい、
NULLではなくなるまで、つまりパケットが流れ、tcpdumpから何か出力されるまで先に行かなくなるのです。
そうなると、下にあるend=time(NULL)を読み込まず、1秒を測れなくなってしまう状況です。
ちなみに、常に何かしらの通信を行わせている状態(fgetsがNULLとならない)では、問題なく1秒をカウントできます。


while(i<150)
{
   start = time(NULL);
   end = time(NULL);

   while((int)(end - start) <1) //1秒間計測
   {
      temp = fgets(buf,150,pp); //tcpdumpから1行(1パケット分)をbufへ取得
      src = strstr(buf,srcstr);//bufから、"ack"を検索

      if(src != NULL && strcmp(buf,cmp) !=0) //検索結果がNULLでなく、前回から変更がある場合
      {
         num_ack[i] += 1;
         strcpy(cmp,buf);
      }
      end = time(NULL);
   }
   i += 1;
}

投稿日時 - 2013-01-18 23:48:49

あなたにオススメの質問