2010-02-25 87 views
3

我做了自己的Socket类,能够发送和接收HTTP请求。 但我仍然有一些问题。下面的代码(我的接收函数)仍然是错误的,有时会崩溃。 我试过调试它,但它必须在指针算术/内存管理的某个地方。C C++ - TCP Socket类:接收问题

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)全包(?

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

感谢您的帮助。

+0

的memcpy(vpszRecvd,TempBuf,结果); 我现在编辑它。 – maxedmelon 2010-02-25 12:29:14

+3

这有一个'C++'标签。所以,通过使用'std :: vector '来缓解所有的内存麻烦。如果你需要传递'char *'给C函数,使用'resize()'成员函数来设置它的大小并使用'&v [0]'(或'&v.begin()'):'recv(this- Socket,&v [0],v.size(),0)' – sbi 2010-02-25 12:30:14

+0

就像我说的,我不能用矢量的东西来做。它必须用字符来完成。 向量/字符串在这里会很棒:-( – maxedmelon 2010-02-25 12:35:54

回答

7

你不初始化temp,并且最重要的是,你要realloc通话错误。它应该是:

temp = realloc (temp, recvsize+1); 

当你调用realloc你有,你扔掉的新地址,并有一个很好的机会,旧的地址现在已被释放。当您尝试对其进行解除引用时,所有投注都将关闭。

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

请记住,realloc已经返回一个新的指针,它可以给你同样的指针,如果,例如,有块之后是足够的自由空间来满足新的大小,或者如果你”重新缩小尺寸。

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

temp = realloc (temp, newsize); 

将导致内存泄漏,当它返回NULL(它不不要释放旧的块)。

一些其他的东西:

  • 你很少需要使用calloc,尤其是在这种情况下,因为你复制在内存反正。
  • 类似地,如果您立即在memcpy之上,则不需要memset内存块为0。
  • 为您提供初始化tempNULL,您可以只使用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); 
} 
+0

现在改变了。它说2663 recv'd字节(应最大像218或400)和一个静态字符串。对于正常的http请求, 看起来不太好。 – maxedmelon 2010-02-25 12:47:34

+0

@maxedmelon,你是否改变了reallocs? – paxdiablo 2010-02-25 12:51:34

+0

yepp ...把​​它们都改成了 – maxedmelon 2010-02-25 12:51:58

0

最近我有这个确切的问题。

Realloc很慢。 Recv很快。数百次重新分配会导致一次崩溃。

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

听起来不错,有任何示例代码吗?我猜我的代码是所以有些东西会崩溃 我甚至没有发现一个接收代码,基于字符,接收到一个完整的数据包 – maxedmelon 2010-02-25 12:25:49

+0

为什么几百个reallocs第二次崩溃? – sth 2010-02-25 12:34:28

+0

我对这一个... – 2010-02-25 12:37:06