C 套接字UDP客户端内存泄漏
我用C编写了一个UDP套接字客户端服务器。在很长一段时间内(例如:1周),客户端每秒向服务器发送一个查询。 我的代码运行得很好,但我可以在时间轴上看到ram显著增加,大约14小时后,内存增加到150M左右 增量在客户端,服务器工作正常 我需要检测导致此问题的原因,因为程序将运行很长时间 我的代码出了什么问题 这是我在客户端的代码:C 套接字UDP客户端内存泄漏,c,sockets,udp,C,Sockets,Udp,我用C编写了一个UDP套接字客户端服务器。在很长一段时间内(例如:1周),客户端每秒向服务器发送一个查询。 我的代码运行得很好,但我可以在时间轴上看到ram显著增加,大约14小时后,内存增加到150M左右 增量在客户端,服务器工作正常 我需要检测导致此问题的原因,因为程序将运行很长时间 我的代码出了什么问题 这是我在客户端的代码: int consultar_servidor(char *t1_str_) { struct timeval t_ini, t_fin, tv; do
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;
}
}
}
主要问题是你打电话
close(clientSocket);
对于代码的所有分支,除非您使用recvfrom
成功读取数据,并使用0
从consultar\u servidor()
返回数据。因此,套接字永远不会关闭,并且存在套接字描述符泄漏
代码中可能还有其他错误,请确保在valgrind下对其进行测试
我建议重新构造代码以避免重复,并帮助捕获此类错误。例如,一个选项是将清理代码移动到单独的函数。另一种选择是使用
goto
,除非你偏执于代码中没有goto
。主要问题是你调用
close(clientSocket);
对于代码的所有分支,除非您使用recvfrom
成功读取数据,并使用0
从consultar\u servidor()
返回数据。因此,套接字永远不会关闭,并且存在套接字描述符泄漏
代码中可能还有其他错误,请确保在valgrind下对其进行测试
我建议重新构造代码以避免重复,并帮助捕获此类错误。例如,一个选项是将清理代码移动到单独的函数。另一种选择是使用
goto
,除非您担心代码中没有goto
。我在发布的代码中看不到任何实际内存分配,因此,如果存在直接内存泄漏,它一定是由程序中其他地方的问题引起的
正如@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()的所有调用
然后运行程序,并查看其标准输出。如果调试输出中打印的数字不断增加,则说明程序正在泄漏套接字,您需要找出原因/方法并修复它。如果没有,那么您在其他地方还有一些问题。我在发布的代码中没有看到任何实际的内存分配,因此,如果存在直接内存泄漏,那么它一定是由程序中其他地方的问题引起的 正如@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()的所有调用
然后运行程序,并查看其标准输出。如果调试输出中打印的数字不断增加,则说明程序正在泄漏套接字,您需要找出原因/方法并修复它。如果没有,那么其他地方还有一些问题。您在哪里分配字符*t1\u str?(您在哪里和如何调用
int consultar\u servidor(char*t1\u str\u)
?现在添加主要部分以完成代码您在哪里分配char*t1\u str\u
?(您在哪里和如何调用int consultar\u servidor(char*t1\u str\u)
?现在添加主要部分以完成代码,更不用说“|0000000000000000 | 0000000000000000 | 0000000000000000”
是52字节,而不是51字节,最大长度值是19个字符,因此整个字符串可能超过67个字符。可能还有其他问题……我需要在没有错误发生时保持套接字打开,这就是不使用close(clientSocket)的原因在recvfrom之后。我这样做是为了避免每次查询都打开和关闭套接字,并节省处理时间。您认为有什么方法可以做到这一点吗?我当时犯了错误,但从您提供的代码中根本不清楚这一点(作为一个说英语的人,很难猜测您从未显示过的明显全局变量cont_parametros
的含义)。如果您关心性能,为什么每次都要重新连接并取消阻止套接字?这里可能会发生一些有趣的事情,但如果没有更明确的问题,人们将不会主动为您调试代码。或者,可能更简单,您可以始终关闭并重新打开套接字。cont_参数是一个全局变量le,它就像一个确定错误发生时间的标志。现在我要用更简单的方法,关闭并重新打开套接字。几个小时后,我将观察程序的行为并再次对您进行注释。更不用说“|0000000000000000 | 0000000000000000 | 0000000000000000000000“
是52字节,而不是51字节,最大长度值是19个字符,因此整个字符串可能超过67个字符。可能还有其他问题……我需要在没有错误发生时保持套接字打开,这就是不使用close(clientSocket)的原因。”在recvfrom之后。我这样做是为了避免每次查询都打开和关闭套接字,并节省处理时间。您认为有什么方法可以做到这一点吗?我当时犯了错误,但从您提供的代码中根本不清楚这一点(作为一个说英语的人,很难猜测您从未显示过的明显全局变量cont_parametros
的含义)。