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

发布于 2024-08-23 02:58:47 字数 1524 浏览 5 评论 0原文

我创建了自己的 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() 接收完整的数据包?

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

感谢您的帮助。

I did my own Socket class, to be able to send and receive HTTP requests.
But I still got some problems. The following code (my receive function) is still buggy, and crashing sometimes.
I tried debugging it, but it must be somewhere in the pointer arithmetics / memory management.

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

Does anybody see anything that could cause the crash, or does anyone have a better / faster / smaller and stable example how to receive a full packet via recv()?

I can't use strings, it must be done with chars, though.

Thanks for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

油饼 2024-08-30 02:58:47

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

temp = realloc (temp, recvsize+1);

当您像以前那样调用 realloc 时,您会丢弃新地址,并且旧地址现在很可能已被释放。当您尝试取消引用它时,所有的赌注都会消失。

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

请记住,realloc 没有返回新指针,它可能会为您提供相同的指针,例如,如果块后有足够的可用空间满足新尺寸或者您正在减小尺寸。

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

temp = realloc (temp, newsize);

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

其他一些事情:

  • 您很少需要使用 calloc,特别是在这种情况下,因为无论如何您都会复制内存。
  • 同样,如果您立即对其进行memcpy,则无需将内存块memset设置为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);
}

You don't initialise temp and, on top of that, your call to realloc is wrong. It should be:

temp = realloc (temp, recvsize+1);

When you call realloc as you have, you throw away the new address and there's a good chance that the old address has now been freed. All bets are off when you then try to dereference it.

The reason realloc returns a new address is because expansion of the buffer may necessitate it being moved if that current block is surrounded in the memory arena (in other words, it can't just expand into a free block following it). In that case, a new block will be created in the arena, the contents transferred from the old block and the old block freed. You have to get the return value from realloc in case that happens.

Keep in mind that realloc doesn't have to return a new pointer, it may give you the same pointer if, for example, there was enough free space after the block to satisfy the new size or if you're reducing the size.

It can also return NULL if it can't expand the block, you should watch out for that as well, especially since:

temp = realloc (temp, newsize);

will result in a memory leak when it returns NULL (it doesn't free the old block).

A few other things:

  • you rarely need to use calloc, especially in this case since you're copying over the memory anyway.
  • similarly, you don't need to memset a memory chunk to 0 if you're immediately going to memcpy over it.
  • provided you initialise temp to NULL, you can just use realloc without testing it. That's because realloc(NULL,7) is identical to malloc(7) - realloc is perfectly capable of starting with a null pointer.
  • since you don't need calloc, this is for education only - sizeof(char) is always 1 by definition.
  • you seem to be doing an awful lot of unnecessary copying of data.

Why don't we start with something a bit simpler? Now, this is totally from my head so there may be some bugs but it's at least cut down from the memory-moving behemoth in the question :-) so should be easier to debug.

It's basically broken down into:

  • initialise empty message.
  • enter infinite loop.
    • get a segment.
    • if error occurred, free everything and return error.
    • if no more segments, return current message.
    • create space for new segment at end of message.
    • if no space could be created, free everything and return empty message.
    • append segment to message and adjust message size.

and the code looks like this:

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);
}
一杆小烟枪 2024-08-30 02:58:47

我最近遇到了这个问题。

重新分配很慢。接收速度很快。每秒数百次重新分配将会崩溃。

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

I had this exact problem very recently.

Realloc is slow. Recv is fast. Several hundred reallocs a second WILL crash.

calloc() not just recvsize + 1 but a buffer of a couple kilobytes extra. realloc() only when the buffer would be filled/overflow, and give it another few kilobytes extra on each realloc().

the below is a piece of code I use to append data to my output stream, but input should be very similar. (as a note, buf_out_size is the size of allocated buffer, buf_out_len is the amount of data currently in the buffer.)

    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
        }
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文