2010-02-25 77 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