2014-02-06 90 views
2

第一個問題:我在TCP中的緩衝區之間感到困惑。我試圖解釋我的問題,我閱讀了這個文檔TCP Buffer,作者說了很多關於TCP緩衝區,這對於初學者來說很好,並且是一個很好的解釋。我需要知道的是,這個TCP緩衝區與我們在基本客戶服務器程序(Char *buffer[Some_Size])中使用的緩衝區是相同的緩衝區,還是由TCP內部持有的一些不同的緩衝區。TCP中的垃圾值和緩衝區差異

第二個問題的是,我從客戶端通過套接字發送帶有前綴長度(This is data From me)的字符串數據到服務器,當我在控制檯打印我的數據我串沿着它打印一些垃圾值也是這樣"This is data From me zzzzzz 1/2 1/2....." ?。不過,我通過右移char *recvbuf = new char[nlength>>3];nlength到3位來修復它但爲什麼我需要這樣做?

第三個問題與第一個問題相關,如果有沒有像TCP緩衝和它的只有約Char *buffer[some_size]然後什麼區別我的程序會使用這樣的靜態內存分配緩衝注意到,通過使用動態內存分配緩衝區使用char *recvbuf = new char[nlength];。總之這是最好的,爲什麼?

客戶端代碼

int bytesSent; 
int bytesRecv = SOCKET_ERROR; 
char sendbuf[200] = "This is data From me"; 

int nBytes = 200, nLeft, idx; 
nLeft = nBytes; 
idx = 0; 
uint32_t varSize = strlen (sendbuf); 
bytesSent = send(ConnectSocket,(char*)&varSize, 4, 0); 
assert (bytesSent == sizeof (uint32_t)); 
std::cout<<"length information is in:"<<bytesSent<<"bytes"<<std::endl; 
// code to make sure all data has been sent 
    while (nLeft > 0) 
{ 
    bytesSent = send(ConnectSocket, &sendbuf[idx], nLeft, 0); 
    if (bytesSent == SOCKET_ERROR) 
    { 
     std::cerr<<"send() error: " << WSAGetLastError() <<std::endl; 
     break; 
    } 
    nLeft -= bytesSent; 
    idx += bytesSent; 
} 

std::cout<<"Client: Bytes sent:"<< bytesSent; 

Server代碼:

int bytesSent; 
char sendbuf[200] = "This string is a test data from server"; 
int bytesRecv; 
int idx = 0; 
uint32_t nlength; 
int length_received = recv(m_socket,(char*)&nlength, 4, 0);//Data length info 
char *recvbuf = new char[nlength];//dynamic memory allocation based on data length info 
//code to make sure all data has been received 
while (nlength > 0) 
{ 
    bytesRecv = recv(m_socket, &recvbuf[idx], nlength, 0); 

    if (bytesRecv == SOCKET_ERROR) 
    { 
     std::cerr<<"recv() error: " << WSAGetLastError() <<std::endl; 
     break; 
    } 
    idx += bytesRecv; 
    nlength -= bytesRecv; 
} 

    cout<<"Server: Received complete data is:"<< recvbuf<<std::endl; 
    cout<<"Server: Received bytes are"<<bytesRecv<<std::endl; 
    WSACleanup(); 
    system("pause"); 
    delete[] recvbuf; 
    return 0; 

}

+0

檢查['ntohl()','htonl()'](http://stackoverflow.com/questions/14173441/what-does-ntohluint32-t-do)正確管理消息長度前綴。 –

+0

我知道他們(ntohl和htonl()),但我真的需要他們,因爲我的客戶端服務器在相同的Windows架構。不是嗎? – User

+0

他們沒有必要在同一臺主機上運行這些東西,但網絡編程的目的通常是在不同主機之間交換數據。 –

回答

1

第一個問題:我很困惑TCP之間的緩衝區。我試圖 解釋我的問題,我讀了這個文檔TCP緩衝區,作者說一個 很多關於TCP緩衝區,這很好,一個 初學者很好的解釋。我需要知道的是,這個TCP緩衝區與我們在基本客戶端服務器程序(Char *緩衝區[Some_Size])中使用的緩衝區是 相同的緩衝區,還是由TCP內部持有的一些不同的緩衝區。

當你調用send()時,TCP堆棧會將字節數組中的一些字節複製到內核緩衝區中,send()將返回它複製的字節數。然後,TCP堆棧將盡可能快地處理這些內核字節在網絡中的傳輸。請注意,send()的返回值爲而不是保證與您在傳遞給它的長度參數中指定的字節數相同;它可能會更少。同樣重要的是要注意,send()的返回值不是而不是意味着很多字節已經到達接收程序;而只是表示內核已經接受並將嘗試傳遞的字節數。同樣,recv()只是將一些字節從內核緩衝區複製到指定的數組中,然後將它們從內核緩衝區中刪除。同樣,複製的字節數可能少於要求的字節數,並且通常與send()在任何特定的send()調用中傳遞的字節數不同。 (例如,如果發件人send()和他的send()返回1000,那可能導致你調用recv()兩次,並且recv()每次返回500,或者recv()可能返回250四次,或者,990,9),或者你能想到的任何其他組合最終加起來1000)

我的第二個問題是,我從發送帶有前綴 長度的字符串數據(這是數據從我這裏)客戶端通過套接字到服務器,當 我打印我的數據在控制檯與我的字符串一起打印一些垃圾 值也像這樣「這是數據從我zzzzzz 1/2 1/2 .....」? 但是我通過右移char * recvbuf = new char [nlength >> 3]; nlength到3位,但爲什麼我需要這樣呢?

就像Joachim所說的,這是因爲C字符串依賴於存在NUL-終止字節(即零字節)來表示它們的結束。您正在接收strlen(sendbuf)字節,並且strlen()返回的值不包含NUL字節。當接收者的字符串打印例程試圖打印字符串時,它會一直打印,直到在內存中稍後的某個地方發現NUL字節(偶然);在此期間,您可以看到該點之前的所有內存中的隨機字節。要解決此問題,請將您的發送字節計數器增加到(strlen(sendbuf)+1),以便接收NUL終止符字節,或者讓接收器手動將NUL字節放在字符串末尾它已經收到了字符串的所有字節。無論哪種方式都是可以接受的(後一種方式可能會稍微偏好,因爲接收方不依賴發送方做正確的事情)。

請注意,如果您的發送者總是發送200個字節而不是字符串中的字節數,那麼如果接收者想要接收多個數據塊,則需要總是接收200個字節;否則,當它嘗試接收下一個塊時,它將首先獲得所有額外的字節(在字符串之後),然後纔會獲得下一個塊的發送長度字段。

我的第三個問題與第一個問題相關,如果有 沒有像TCP緩衝和它大約只有字符*緩衝區[some_size] 然後什麼區別我的程序會使用這樣的靜態 內存分配緩衝區通知並通過使用動態內存分配緩衝區 使用char * recvbuf = new char [nlength] ;.總之哪個最好, 爲什麼?

在性能方面,根本沒有差別。 send()和receive()不關心你傳遞給它們的指針是指向堆還是堆棧。

在設計方面,有一些權衡:如果您使用新的,如果您在完成緩衝區時不總是調用delete [],則可能會泄漏內存。 (當拋出異常或者發生錯誤路徑時,這可能會發生。另一方面,將緩衝區放置在堆棧上可以保證不會泄漏內存,但是堆棧上可用的空間量是有限的,所以一個非常龐大的陣列可能會導致程序運行堆棧空間不足並導致崩潰。在這種情況下,堆棧中的單個200字節數組沒有問題,所以這就是我會使用的。

+0

對於第二個問題,它通過在recv()之後放置'recvbuf [nlength] = 0;'來工作。非常感謝 ! – User

4

您從客戶端發送200個字節,無條件地,但是在服務器只接收的實際長度字符串,並且那個長度確實是而不是包括字符串終止符。

因此,首先您不會收到所有發送的數據(這意味着您將填充系統緩衝區),然後您不會正確終止字符串(嘗試在嘗試時導致「垃圾」輸出打印字符串)。

要解決此問題,客戶端只發送字符串的實際長度(值爲varSize),並在接收服務器中爲終止符分配一個字符,您當然需要添加該字符。

+0

謝謝!你能否再次看看代碼,我刪除了你建議更改的功能,因爲那是錯誤的。現在我已經完成了這個'char sendbuf [30] =「這是來自我的數據\ 0」; '和'char * recvbuf = new char [nlength + 1]; '在客戶端和服務器分別但仍然是同一件事,不知道y? – User

+0

@ user3232405您不必在客戶端的字符串文字中手動添加終結符,編譯器會自動將其放入該位置。但是你*需要發送它(即發送'varSize + 1'字節)**或**手動添加*到服務器*中。 –

+0

Joachim Pileborg:對不起!仍然沒有工作。更重要的是,你能否提出一些其他問題。 – User