简体   繁体   English

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

[英]C C++ - TCP Socket Class : Receive Problem

I did my own Socket class, to be able to send and receive HTTP requests. 我做了自己的Socket类,以便能够发送和接收HTTP请求。 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()? 是否有人看到任何可能导致崩溃的信息,还是有人有一个更好,更快,更小,更稳定的示例,如何通过recv()接收完整的数据包?

I can't use strings, it must be done with chars, though. 我不能使用字符串,但是必须用字符完成。

Thanks for your help. 谢谢你的帮助。

You don't initialise temp and, on top of that, your call to realloc is wrong. 您不初始化temp ,最重要的是,您对realloc的调用是错误的。 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. 当您按原样调用realloc ,您将丢弃新地址,并且很有可能旧地址现在已被释放。 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). realloc返回一个新地址的原因是因为如果当前块被内存区域包围,则缓冲区的扩展可能需要将其移动(换句话说,它不能随即扩展为空闲块)。 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. 如果发生这种情况,您必须从realloc获取返回值。

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. 请记住, realloc 没有返回一个新的指针,它可以给你同样的指针,如果,例如,有足够的自由空间块之后,以满足新的大小或者如果你缩小尺寸。

It can also return NULL if it can't expand the block, you should watch out for that as well, especially since: 如果无法扩展该块,它也可以返回NULL,您也应该注意这一点,特别是因为:

temp = realloc (temp, newsize);

will result in a memory leak when it returns NULL (it doesn't free the old block). 返回NULL时将导致内存泄漏(它不会释放旧块)。

A few other things: 其他一些事情:

  • you rarely need to use calloc , especially in this case since you're copying over the memory anyway. 您几乎不需要使用calloc ,在这种情况下尤其如此,因为无论如何您都在内存中进行复制。
  • similarly, you don't need to memset a memory chunk to 0 if you're immediately going to memcpy over it. 同样的,你不需要memset一个内存块为0,如果你马上要memcpy过去。
  • provided you initialise temp to NULL , you can just use realloc without testing it. 如果您将temp初始化为NULL ,则可以仅使用realloc而不进行测试。 That's because realloc(NULL,7) is identical to malloc(7) - realloc is perfectly capable of starting with a null pointer. 这是因为realloc(NULL,7)malloc(7)相同realloc完全能够从空指针开始。
  • since you don't need calloc , this is for education only - sizeof(char) is always 1 by definition. 因为您不需要calloc ,所以这仅用于教育sizeof(char)根据定义始终为 1。
  • 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);
}

I had this exact problem very recently. 我最近有这个确切的问题。

Realloc is slow. 重新分配很慢。 Recv is fast. 接收快。 Several hundred reallocs a second WILL crash. 数百个重新分配将导致第二次WILL崩溃。

calloc() not just recvsize + 1 but a buffer of a couple kilobytes extra. calloc()不仅可以接收大小+ 1,而且还可以缓冲区几千字节。 realloc() only when the buffer would be filled/overflow, and give it another few kilobytes extra on each realloc(). 仅当缓冲区将被填充/溢出时才使用realloc(),并在每个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.) (请注意,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
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM