2017-08-21 14 views
0

CでUDPソケット・クライアント・サーバをコード化しました。クライアントが毎秒サーバーにクエリを長時間(例:1週間)送信しました。 私のコードは正常に動作しましたが、タイムラインでラムがかなり増えたことがわかります。約14時間後にメモリは約150Mに増加しました。ソケットUDPクライアント・メモリ・リーク

増分はクライアント側であり、サーバーは正常に動作しています。

プログラムが長時間実行されるため、この問題の原因を検出する必要があります。

私のコードで何が間違っていますか?

これは、クライアント側での私のコードです:

int consultar_servidor(char *t1_str_) 
{ 
    struct timeval t_ini, t_fin, tv; 
    double secs; 
    char cadena_enviada[67]; 
    char cadena_recibida[67]; 
    char tx_str[51]= "|0000000000000000|0000000000000000|0000000000000000"; 
    int validacion, i; 

    long long int t4; 
    char t4_str[20]; 

    char t2_str_rec[20]; 
    char t2_pps_str_rec[20]; 

    char t3_str_rec[20]; 

    int nBytes, numfd; 


    if (t1_str_ != 0) 
    { 
     strcpy(cadena_enviada,t1_str_); 
     strcat(cadena_enviada,tx_str); 
    } 
    else 
    { 
     error("Error recepcion t1"); 
     return 1; 
    } 
    if (cont_parametros == 0) 
    { 
     set_param(); 
    } 

    if (connect(clientSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0) 
     error("Error connecting socket"); 

    if (sendto(clientSocket,cadena_enviada,sizeof(cadena_enviada),0,(struct sockaddr *)&serverAddr,addr_size) < 0) 
    { 
     close(clientSocket); 
     error("Error sentto function"); 
     cont_parametros = 0; 
     return 1; 
    } 

    /** Socket nonblock **/ 
    int flags = fcntl(clientSocket, F_GETFL, 0); 
    fcntl(clientSocket, F_SETFL, flags | O_NONBLOCK); 
    fd_set readfds; 

    FD_ZERO(&readfds); 
    FD_SET(clientSocket, &readfds); 
    numfd = clientSocket + 1; 

    /** Set 700us to receive **/ 
    tv.tv_sec=0; 
    tv.tv_usec=700000; 

    /** Server send me **/ 
    int recibo = select(numfd, &readfds,NULL,NULL,&tv); 

    switch (recibo) 
    { 
     case -1: 
      /** Error reception **/ 
      error("Error reception"); 
      FD_CLR(clientSocket, &readfds); 
      close(clientSocket); 
      cont_parametros=0; 
      return 1; 
     case 0: 
      /** Timeout and close socket **/ 
      error("Error timeout"); 
      FD_CLR(clientSocket, &readfds); 
      close(clientSocket); 
      cont_parametros = 0; 
      return 1; 
     default: 
      /** If socket contain data **/ 
      if (FD_ISSET(clientSocket, &readfds)) 
      { 
       /** catch t4 **/ 
       t4=ts(); 
       sprintf(t4_str, "%lld", t4); 

       /** Receive server message**/ 
       nBytes = recvfrom(clientSocket,cadena_recibida,sizeof(cadena_recibida),0,NULL, NULL); 
       /** If si a bad data **/ 
       if (nBytes < 0) 
       { 
        error("Error recept data"); 
        FD_CLR(clientSocket, &readfds); 
        close(clientSocket); 
        cont_parametros = 0; 
        return 1;    
       } 

       /** Clean set **/ 
       FD_CLR(clientSocket, &readfds); 

       int i; 

       /** trim t2**/ 
       for(i=17;i<33;i++) t2_str_rec[i-17]=cadena_recibida[i]; 
       t2_str_rec[16]= '\0'; 

       /** trim t3**/ 
       for(i=34;i<51;i++) t3_str_rec[i-34]=cadena_recibida[i]; 
       t3_str_rec[16]= '\0'; 

       printf("%s|%s|%s|%s\n",t1_str_, t2_str_rec, t3_str_rec, t4_str); 
       return 0; 
      } 
    } 
} 

とのparamsソケットを設定する機能:

void set_param() 
{ 
    /** Set client params **/ 
    memset(&local_addr, 0, sizeof(struct sockaddr_in)); 
    local_addr.sin_family = AF_INET; 
    local_addr.sin_port = htons(SRC_PORT); 

    local_addr.sin_addr.s_addr = inet_addr(SRC_IP); 

    /** Configure settings in address struct **/ 
    serverAddr.sin_family = AF_INET; 
    serverAddr.sin_port = htons(DST_PORT); 
    serverAddr.sin_addr.s_addr = inet_addr(DST_IP); 
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero); 

    addr_size = sizeof serverAddr; 

    clientSocket = socket(AF_INET, SOCK_DGRAM, 0); 
    if (clientSocket < 0) 
    { 
     error("Error socket no create"); 
     exit(1);  
    } 
    if (bind(clientSocket, (struct sockaddr *)&local_addr, sizeof(local_addr))< 0) 
    { 
     close(clientSocket); 
     error("Error bind in socket"); 
     exit(1); 
    } 
    /** Socket create OK**/ 
    cont_parametros = 1; 
} 

主要部分

int main(int argc, char* argv[]) 
{ 
    long long int t1;   
    char t1_str[20];    
    while(1) 
    { 
     t1=ts(); 
     sprintf(t1_str, "%lld", t1); 
     consultar_servidor(t1_str); 
     sleep(1); 
    } 
} 
+0

'char * t1_str_'はどこに割り当てますか? (どこで 'int consultar_servidor(char * t1_str_)'と呼んでいますか? –

+0

コードを完成させるために主要部分が追加されました –

答えて

2

主な問題は、あなたが成功しrecvfromでデータを読み取り、consultar_servidor()からコード0を返す場合を除き、コードのすべての支店のための

close(clientSocket); 

を呼び出すことです。その結果、ソケットは決して閉じられず、ソケット記述子のリークが発生します。

コードに他のバグがある可能性がありますので、valgrindでテストしてください。

重複を避け、これらのようなバグを捉えるためにコードを再構成することをお勧めします。たとえば、クリーンアップコードを別の機能に移動するオプションがあります。もう一つの選択肢は、gotocleanup patternを使用することです。

+1

'' | 0000000000000000 | 0000000000000000 | 0000000000000000 "は52バイトで、51ではなく、最大の長さは19文字ですので、文字列全体で67文字を超えることができます。他にも問題があるかもしれません... – rustyx

+0

エラーが発生していない間はソケットを開いたままにしておく必要があります。これはrecvfromの後にclose(clientSocket)を使用しない理由です。私はそれを開き、各クエリごとにソケットを閉じないようにし、処理時間を節約します。あなたはこれを行うための何らかの方法を考えていますか? –

+0

私の間違いでしたが、それはあなたが提供したコードからはっきりとは分かりませんでした(英語の話し手として、あなたが見せない明白なグローバル変数 'cont_parametros'の意味は推測が難しいです)。あなたがパフォーマンスを心配している場合は、なぜソケットを再接続し、毎回そのソケットをブロック解除するのですか?潜在的にここで興味深いことが起こっていますが、より明確な質問がなければ、人々はあなたのコードをデバッグするよう動機づけられることはありません。あるいは、おそらく簡単ですが、いつでもソケットを閉じて再オープンすることができます。 – kfx

0

投稿されたコードに実際のメモリ割り当てがないため、直接メモリリークが発生した場合は、プログラム内の別の場所で問題が発生している必要があります。

@kfxが述べたように、別の可能性はソケットリークです。各ソケットには一定量のRAMを使用するバッファが付属しているため、メモリ使用量が増加することもあります。あなたの中

static int socketCount = 0; 

int debug_socket(int domain, int type, int protocol) 
{ 
    int ret = socket(domain, type, protocol); 
    if (ret >= 0) 
    { 
     ++socketCount; 
     printf("After socket() call succeeded, there are now %i sockets in use by this program\n", socketCount); 
    } 
    else perror("socket() failed!"); 

    return ret; 
} 

int debug_close(int sock) 
{ 
    int ret = close(sock); 
    if (ret == 0) 
    { 
     --socketCount; 
     printf("After close() call succeeded, there now %i sockets in use by this program\n", socketCount); 
    } 
    else perror("close() failed!"); 

    return ret; 
} 

...その後、一時的に(ソケットにすべての呼び出しを置き換える):

あなたのプログラムは、ソケットはあなたのプログラムにこのような何かを追加することです漏れているかどうかをテストする簡単な方法は、 debug_socket()を使用したプログラム、およびdebug_close()を使用したプログラム内のclose()のすべての呼び出し。

次に、プログラムを実行して、stdoutの出力を見てください。デバッグ出力に表示される数字が絶えず増加している場合は、プログラムがソケットをリークしているので、理由と原因を突き止めて修正する必要があります。もしそうでなければ、あなたは他のところでいくつかの問題を抱えています。

+0

あなたの提案@ jeremy-friesnerに感謝します。あなたの2つの関数と変数socketCountを使ってコードを変更し、毎回1と0の間で変更します。 私の手術系の問題が考えられます。私のコードは、ラズベリーのpi3でraspbian 8を実行しています。 –

関連する問題