【问题标题】:Memory Leak - Socket or String related?内存泄漏 - 套接字或字符串相关?
【发布时间】:2015-08-31 18:52:25
【问题描述】:

我有一个简单的函数,可以让我获取服务器上文件的内容。它按我想要的方式工作但是 “Visual Leak Detector”指出在线closeSocket(...)存在内存泄漏。

代码如下:

string executeUrl(const char *url)
{
    SOCKET sConnection;
    char szHeader[500];

    sprintf(szHeader, "GET %s HTTP/1.0\r\n"
    "Host: %s\r\n"
    "User-Agent: Agent\r\n"
    "\r\n", url, HTTPSERVER);

    sConnection = HTTPConnectToServer(HTTPSERVER);
    if (sConnection == 0)
    {
        return "";
    }
    send(sConnection, szHeader, strlen(szHeader), 0);
    char reply[1024];
    ZeroMemory(reply, 1024);
    if (recv(sConnection, reply, 1024, 0) == SOCKET_ERROR)
    {
        return "";
    }
    string returnString(reply);
    closesocket(sConnection);
    WSACleanup();
    return returnString;
}

泄露的数据字符串returnString。所以它要么与字符串相关,要么与closesocket() 相关。

我做了一些阅读,但我无法弄清楚。显然字符串应该照顾好自己,不会导致内存泄漏,不是吗?


编辑:

我尝试了以下代码:http://www.zedwood.com/article/cpp-winsock-basic-http-connection

即使是这段代码也会对我产生内存泄漏。我还测试了一个不同的应用程序(Deleaker),它还告诉我存在相同的内存泄漏。


EDIT2:

刚刚发现它不是唯一一次字符串泄漏内存。这段代码也有问题:

urlString = (string)"http://someurl" + std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(pcName) + (string)"somefile.php";

泄露的数据正是“urlString”所持有的。我完全糊涂了。

【问题讨论】:

  • 确实,字符串不应该泄漏。所以我的钱要么花在 closesocket 上,要么花在检漏仪上。
  • 然而,VLD 中的“数据”块完全包含“returnString”中的内容。我通过将其从“回复”更改来确定。
  • 尝试使用 Visual Studio Express 2013 和 Visual Leak Detector 2.3“编辑”,“未检测到内存泄漏。” .尝试了这个link,几乎没有修改以根据收到的缓冲区创建字符串 - “未检测到内存泄漏。”
  • 我一无所知...也许我的电脑上的某些东西被窃听了。
  • 派对迟到了,但我遇到了 wstring_convert 像塞夫一样泄漏的问题!但仅在 x64 版本中,在 32 位和调试中都可以。开始认为这是 MS 实现中的错误。

标签: c++ string sockets memory-leaks


【解决方案1】:

我预计会有一些未定义的行为。您已阅读 1024 个字符来回复。然后,您使用期望以空字符结尾的字符序列的构造函数。你怎么能确定回复是空终止的字符序列?

作为一个快速测试尝试读取 1023 个字符而不是 1024 个字符,这里:

if (recv(sConnection, reply, 1023, 0) == SOCKET_ERROR)

【讨论】:

  • 试过了,同样的内存泄漏。
【解决方案2】:

当您从套接字读取时,您不应该假设您已经在一次读取中收到了所有数据。

在这种情况下,服务器可能发送了超过 1024 个字节,而您读取的正是 1024。这里可能发生的情况是您的缓冲区不会以 NULL 结尾,您尝试用它初始化一个字符串,但没有给它正确的长度。 Afaik 这是未定义的行为,因此您可能首先要修复它。此外,您应该读取服务器发送的所有数据。

char reply[1025];
ZeroMemory(reply, 1025);
string returnString;
int val = 0;
do 
{
    val = recv(sConnection, reply, 1024, 0);
    if (val == SOCKET_ERROR)
    {
        // Treat errors
    }
    strcat (returnString, reply);
    ZeroMemory(reply, 1025);
}
while (val == 1024); 

编辑:此代码错误。如果没有与服务器预先交换消息,很难判断消息应该包含多少字节或内容。在这段代码的情况下,如果服务器正好发送了 1024 个字节,那么 recv 将在下一次迭代中被阻塞,因为没有什么可以读取的。 网络协议建立了这些基本的通信规则。例如,http 协议规定消息的第一部分由标头组成,每个标头以 CRLF 对结尾,标头的结尾由 2 个 CRLF 对标记。为了使我的代码正确,我必须解析标头并提取 Content-Length 值,然后读取消息正文,直到达到指定的长度。

【讨论】:

  • 如果我不关心前 1024 个字节之后的任何内容怎么办?我可以读取前 1024 个字节并将接收到的内容作为字符串返回而不会发生内存泄漏吗?
【解决方案3】:

此代码包含潜在的缓冲区溢出,您应该使用字符串构造函数:

string returnString(reply, 1024);

您正在使用的构造函数假定它被传递一个以 null 结尾的字符串,根据 recv() 的结果,情况可能并非如此。

编辑:closesocket 的返回值是多少?您确定在程序终止之前套接字实际上已关闭吗?如果在 closesocket 调用和结束函数之间等待几秒钟会发生什么?

【讨论】:

  • 不修复内存泄漏问题。
【解决方案4】:

最好遵循 MSDN 中的一些示例代码,即:

https://msdn.microsoft.com/pl-pl/library/windows/desktop/ms737591(v=vs.85).aspx

或至少尝试运行此示例以检查它是否也会对您产生泄漏。

我看到你错过了shutdown 电话,它是否会导致泄漏 - 我不确定,你可以从它的文档中阅读:

为确保在连接的套接字关闭之前发送和接收所有数据,应用程序应在调用 closesocket 之前使用 shutdown 关闭连接。

顺便说一句。使用 Win API 时,请务必检查所有可能从 api 函数返回的错误代码。

【讨论】:

  • 尝试添加关机调用,但没有任何改变。稍后我会尝试使用 msdn 示例。
猜你喜欢
  • 2014-04-30
  • 1970-01-01
  • 2015-08-26
  • 1970-01-01
  • 2011-08-29
  • 2013-04-03
  • 1970-01-01
  • 2023-03-29
  • 2011-06-12
相关资源
最近更新 更多