c++ - C C++ - TCP 套接字类 : Receive Problem

标签 c++ c networking sockets winsock

我编写了自己的 Socket 类,以便能够发送和接收 HTTP 请求。 但我仍然遇到了一些问题。下面的代码(我的接收函数)仍然有问题,有时会崩溃。 我试过调试它,但它一定在指针算法/内存管理中的某个地方。

int Socket::Recv(char *&vpszRecvd)
{
 //vpszRecvd = NULL;
 int  recvsize = 0;
 char TempBuf[1024];
 int  Result = 0;
 char* temp;


 do
 {
  memset(TempBuf, 0, sizeof(TempBuf));

  Result = recv( this->sSocket, TempBuf, sizeof(TempBuf) -1, 0 );
  if (recvsize == 0)
   recvsize = Result;

  if ( Result > 0 )
  {
   if ( vpszRecvd != NULL )
   {
    if (temp == NULL)
    {
     temp = (char*)calloc(recvsize + 1, sizeof(char));
    }
    else
    {
     realloc(temp, recvsize + 1);
    }
    if (temp == NULL)
     return 0;

    memcpy(temp, vpszRecvd, recvsize);
    realloc(vpszRecvd, recvsize + Result);

    if (vpszRecvd == NULL)
     return 0;

    memset(vpszRecvd, 0, recvsize + Result);
    memcpy(vpszRecvd, TempBuf, Result);
    memcpy(vpszRecvd + recvsize, TempBuf, Result);
    recvsize += Result; 
   }
   else
   {
    realloc(vpszRecvd, Result);

    if (vpszRecvd == NULL)
     return 0;

    memset(vpszRecvd, 0, Result);
    memcpy(vpszRecvd, TempBuf, Result);
    recvsize += Result;
   }
  }
  else if (  Result == 0 )
  {
   return recvsize;

  }
  else //if (  Result == SOCKET_ERROR )
  {
   closesocket(this->sSocket);
   this->sSocket = INVALID_SOCKET;
   return SOCKET_ERROR;
  }
 }
 while( Result > 0 );

 return recvsize;
}

有没有人看到任何可能导致崩溃的东西,或者有没有人有更好/更快/更小和稳定的示例如何通过 recv() 接收完整的数据包?

我不能使用字符串,但必须使用字符。

感谢您的帮助。

最佳答案

你没有初始化 temp而且,最重要的是,你调用realloc是错的。应该是:

temp = realloc (temp, recvsize+1);

当您调用 realloc 时正如您所做的那样,您丢弃了新地址,很可能旧地址现在已被释放。当您随后尝试取消引用它时,所有的赌注都没有了。

原因realloc返回一个新地址是因为缓冲区的扩展可能需要移动它,如果当前 block 被包围在内存区域中(换句话说,它不能只是扩展到它后面的空闲 block )。在这种情况下,将在竞技场中创建一个新 block ,从旧 block 传输内容并释放旧 block 。您必须从 realloc 获取返回值以防万一。

请记住 realloc 没有返回一个新的指针,它可能会给你相同的指针,例如,如果 block 后有足够的可用空间来满足新的大小,或者如果你正在减小大小.

如果它不能扩展 block ,它也可以返回 NULL,你也应该注意这一点,特别是因为:

temp = realloc (temp, newsize);

当它返回 NULL 时会导致内存泄漏(它不会释放旧 block )。

其他一些事情:

  • 你很少需要使用 calloc ,尤其是在这种情况下,因为无论如何您都在复制内存。
  • 同样,您不需要 memset如果您立即转到 memcpy,则内存块变为 0在它上面。
  • 前提是你初始化tempNULL , 你可以只使用 realloc没有测试它。那是因为 realloc(NULL,7)malloc(7) 相同- realloc完全可以从空指针开始。
  • 因为你不需要calloc ,这仅用于教育 - sizeof(char)根据定义,总是 1。
  • 你似乎做了很多不必要的数据复制。

我们为什么不从更简单的事情开始呢?现在,这完全是我的想法,所以可能有一些错误,但它至少从问题中的内存移动庞然大物中减少了 :-) 所以应该更容易调试。

基本上分为:

  • 初始化空消息。
  • 进入无限循环。
    • 获取 segmentation 。
    • 如果发生错误,释放一切并返回错误。
    • 如果没有更多的段,返回当前消息。
    • 在消息末尾为新段创建空间。
    • 如果无法创建空间,则释放所有内容并返回空消息。
    • 将段附加到消息并调整消息大小。

代码如下所示:

int Socket::Recv(char *&vpszRecvd) {
    int  recvsize = 0;
    char TempBuf[1024];
    int  Result = 0;
    char *oldPtr;

    // Optional free current and initialise to empty.

    //if (vpszRecvd != NULL) free (vpszRecvd);
    vpszRecvd = NULL;

    // Loop forever (return inside loop on end or error).

    do {
        Result = recv( this->sSocket, TempBuf, sizeof(TempBuf) -1, 0 );

        // Free memory, close socket on error.

        if (Result < 0) {
            free (vpszRecvd);
            closesocket(this->sSocket);
            this->sSocket = INVALID_SOCKET;
            return SOCKET_ERROR;
        }

        // Just return data and length on end.

        if (Result == 0) {
            return recvsize;
        }

        // Have new data, use realloc to expand, even for initial malloc.

        oldPtr = vpszRecvd;
        vpszRecvd = realloc (vpszRecvd, recvsize + Result);

        // Check for out-of-memory, free memory and return 0 bytes.

        if (vpszRecvd == NULL) {
            free (oldPtr);
            return 0;
        }

        // Append it now that it's big enough and adjust the size.

        memcpy (&(vpszRecvd[recvsize], TempBuf, Result);
        recvsize += Result;
    } while (1);
}

关于c++ - C C++ - TCP 套接字类 : Receive Problem,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2333827/

相关文章:

c++ - 这个表达式计算什么

c - 服务放入安装功能后无法启动

c - 使用毫();按下按钮后延迟

时间:2019-05-10 标签:c++lambda: Currying sum function: returns different results using capture by value vs by reference

c++ - 如何显示目录中的文件列表

c# - C# 和 Java 中的泛型与 C++ 中的模板有什么区别?

c# - 最好在依赖网络的代码中使用 IP 地址或主机名?

bash - 我应该查看什么 snmp OID 以查看我的打印机和交换机是否正在运行

node.js - 完整的 WebRTC 堆栈

c++ - 向函数添加一个参数后编译 C++ 程序时出现问题