内存泄漏 - 套接字还是字符串相关?

时间:2015-08-31 18:52:26

标签: c++ string sockets memory-leaks

我有一个简单的函数,允许我在服务器上获取文件的内容。它的工作方式与我想要的方式但是 “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”所持有的。我完全糊涂了。

4 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:2)

当您从插座阅读时,您不应该假设您已经在一次阅读中收到了所有数据。

在这种情况下,服务器可能已经发送了超过1024个字节并且您正好读取了1024.这里可能发生的是您的缓冲区不会以NULL结尾,并且您尝试使用它初始化字符串并且没有给它正确长度。 Afaik这是未定义的行为,因此它是您可能想要解决的第一件事。此外,您应该阅读服务器发送的所有数据。

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.6</version>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
            </manifest>
            <manifestEntries>
                <mode>development</mode>
                <url>http://sample.com</url>
                <key>value</key>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

修改:此代码错误。如果没有与服务器预先交换消息,则很难确定消息应该有多少字节或内容。对于此代码,如果服务器正好发送1024个字节,则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); 将在下一次迭代时被阻止,因为没有任何内容可读。 网络协议建立这些基本通信规则。例如,http协议规定消息的第一部分由头部组成,每个头部以CRLF对结束,头部的末端由2个CRLF对标记。为了使我的代码正确,我将不得不解析标题并提取Content-Length值,然后读取消息正文,直到达到指定的长度。

答案 2 :(得分:1)

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

string returnString(reply, 1024);

您正在使用的构造函数假定它正在传递一个以空字符结尾的字符串,根据recv()的结果可能不是这种情况。

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

答案 3 :(得分:0)

最好从MSDN中获取一些示例代码,即:

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

或者至少尝试运行此示例以检查它是否也会导致泄漏。

我发现你错过了shutdown电话,是否可能导致泄密 - 我不确定,从你的文档中你可以读到:

  

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

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