C C ++ - TCP套接字类:接收问题

时间:2010-02-25 12:13:00

标签: 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()接收完整数据包?

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

感谢您的帮助。

2 个答案:

答案 0 :(得分:7)

您没有初始化temp,最重要的是,您对realloc的呼叫是错误的。它应该是:

temp = realloc (temp, recvsize+1);

当您按原样拨打realloc时,会丢弃新地址,现在旧地址已被释放的可能性很大。当你试图取消引用时,所有的赌注都会被取消。

realloc返回新地址的原因是因为如果当前块被包含在内存区域中,则缓冲区的扩展可能需要移动缓冲区(换句话说,它不能只扩展为空闲块跟着它)。在这种情况下,将在场地中创建一个新块,从旧块传输的内容和旧块被释放。如果发生这种情况,您必须从realloc获取返回值。

请记住,realloc没有来返回一个新指针,例如,如果块之后有足够的可用空间,它可能会给你相同的指针满足新尺寸或缩小尺寸。

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

temp = realloc (temp, newsize);

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

其他一些事情:

  • 您很少需要使用calloc,尤其是在这种情况下,因为您无论如何都在复制内存。
  • 同样地,如果您立即转到memset,则不需要将memcpy内存块设为0。
  • 如果您将temp初始化为NULL,则可以在不对其进行测试的情况下使用realloc。这是因为realloc(NULL,7)malloc(7)相同 - realloc完全能够以空指针开头。
  • ,因为您不需要calloc,这仅适用于教育 - 根据定义,sizeof(char) 总是 1。
  • 您似乎正在进行大量不必要的数据复制。

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

它基本上分解为:

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

,代码如下:

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

答案 1 :(得分:0)

我最近遇到了这个问题。

Realloc很慢。 Recv很快。几百次reallocs第二次WILL崩溃。

calloc()不仅仅是recvsize + 1,还有一个几千字节的缓冲区。 realloc()仅在缓冲区被填充/溢出时,并在每个realloc()上额外提供几千字节。

下面是我用来将数据附加到输出流的一段代码,但输入应该非常相似。 (注意,buf_out_size是分配缓冲区的大小,buf_out_len是当前缓冲区中的数据量。)

    void Netconsole::ParseOutput(int sock, std::string raw)
    {


        //if too small, realloc with raw.size() + BUFFSIZE.
        if (s[sock]->buf_out_len + raw.size() > s[sock]->buf_out_size)
        {
            s[sock]->buf_out_size += raw.size() + BUFFSIZE;
            s[sock]->buf_out = (char*) realloc( s[sock]->buf_out, s[sock]->buf_out_size);
        }

        // append new data to the end of the buffer.
        if(s[sock]->buf_out != NULL)
        {
            memcpy(s[sock]->buf_out + s[sock]->buf_out_len, raw.c_str(), raw.size());
            s[sock]->buf_out_len += raw.size();

        }
        else
        {
            s[sock]->ending = true;
    #if DEBUG_PRINT_TCP
            printf("%s TCP[%d] dies from out of memory, realloc error\r\n",Debug::MTimestamp(),sock);
    #endif
        }
    }