套接字UDP客户端内存泄漏

时间:2017-08-21 18:46:38

标签: c sockets udp

我在C中编写了一个UDP套接字客户端 - 服务器。客户端每秒都向服务器发送一个查询很长一段时间(例如:1周)。 我的代码运行正常,但我可以在时间线上看到内存大幅增加,大约14小时后内存增加到大约150M。

增量在客户端,服务器工作正常。

我需要检测导致此问题的原因,因为程序将会运行很长时间。

我的代码有什么问题?

这是我在客户端的代码:

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);
    }
}

2 个答案:

答案 0 :(得分:2)

主要问题是你打电话

close(clientSocket);

代码的所有分支,除非您使用recvfrom成功阅读数据并从0返回代码consultar_servidor()。因此,套接字永远不会关闭,并且存在套接字描述符泄漏。

代码中可能存在其他错误,请确保在valgrind下进行测试。

我建议重新构建代码以避免重复并帮助捕获这些错误。例如,一个选项是将清理代码移动到单独的函数。另一种选择是使用goto cleanup pattern,除非您对代码中没有goto感到妄想。

答案 1 :(得分: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()临时替换程序中对socket()的所有调用,并使用debug_close()临时替换程序中对close()的所有调用。

然后运行程序,并观察其stdout输出。如果调试输出中打印的数字不断增加,则程序正在泄漏套接字,您需要找出原因/方法并进行修复。如果没有,那么你在其他地方还有其他一些问题。