2013-10-07 41 views
0

我試圖解決奇怪的C編程問題。截斷緩衝區

我在客戶端程序中輸入URL,然後將該URL傳輸到服務器程序。唯一的問題是,當服務器程序收到URL時,它丟失了前兩個字符。因此,如果網址爲http://www.google.com服務器報告收到的是「tp://www.google.com」。

奇怪的是,它不是一些部分發送問題。我正在檢查發送的字節數,並聲稱發送了整個消息。問題是,在接收端,它只聲稱它正在獲取一小部分數據。接收者報告它收到消息長度-2。這裏是在接收端的代碼:

printf("%s \n", "Connected. Receive length of URL to wget."); 
    if ((messageSize = recv(acceptDescriptor, &urlLength, sizeof (int), 0)) == -1) { 
    perror("recv URL length"); 
    exit(1); 
    } 
    urlSizeInt = atoi(urlLength); 
    char url[urlSizeInt]; 
    printf("%s %d \n", "urlSizeInt: ", urlSizeInt); 
    printf("%s \n", "Receive URL to wget."); 

    if((messageSize = recv(acceptDescriptor, &url, 13, 0)) == -1) { 
    perror("recv URL"); 
    exit(1); 
    } 

發送代碼:

printf("%s \n", "Connected"); 
    //connected to first stepping stone in the chain. 
    //transfer the length of the URL 
    if (send(socketDescriptor, urlLengthStr, strlen(urlLengthStr), 0) == -1){ 
    perror("send URL Length"); 
    exit(0); 
    } 

    //transfer the URL 
    printf("%s %d \n", "strenlen(url): ",strlen(url)); 
    printf("%s %s \n", "url: ",url); 
    int sent; 
    int totalSent=0; 
    if((sent=send(socketDescriptor, url, strlen(url), 0))==-1){ 
     perror("send URL"); 
     exit(0); 
    } 

    printf("%s %d \n", "sent: ",sent); 


Send Output: 
Connected 
strenlen(url): 13 
url: http://www.cs 
sent: 13 

Receive Output: 
Connected. Receive length of URL to wget. 
urlSizeInt: 13 
Receive URL to wget. 
messageSize: 11 
URL Received: tp://www.cs 

代碼來編碼長度作爲char用於發送:

char* url = "http://www.cs"; 
int urlLength = strlen(url); 
char* urlLengthStr; 
sprintf(urlLengthStr, "%d", urlLength); 
+0

發送端的代碼是什麼? –

+0

更新了描述。請記住,如果URL的長度是13個字節,那麼發送將報告它已發送13個字節。所以它不會發送截斷的消息。 – natediggs

+0

運行它通過查爾斯代理或wireshark什麼的,所以你可以真正看到發送。 –

回答

2

感謝張貼的完整代碼。問題在於你發送UrlLength的方式。由於您始終使用recvsizeof(int)字節,因此您的第一次讀取消耗了已發送URL的第一個字節。

從頭開始 - 假設您不發送URL長度。

問題:URL是可變長度。接收者怎麼知道它什麼時候被讀取? :發送

這是優良的,除非你編碼長度作爲字符串前的長度,因爲其引入了另一個問題

問題:該URL長度可以是可變長度(「1」 ,「12」,「1234」)。接收者怎麼知道它什麼時候被讀取? 解決方案:我們不是來過的地方......

有一對夫婦的出路,這個遞歸問題:

解決方案一:編碼URL長度爲固定大小字段。 (你可以發送一個int的二進制表示,儘管要注意字節順序問題 - 或者你可以把它編碼爲一個固定寬度的ascii字段,例如:「00000124」

TX(忽略JimR提到的字節順序問題)

int urlLength = strlen(url); 
    send(socketDescriptor, &urlLength, sizeof(int), 0) 

RX:

int urlLength; 
    recv(socketDescriptor, &urlLength, sizeof(int), 0) 

解決方案b:直到達到終止使用的終止符(通常爲空,或者換行)以說明該URL的末尾剛看了一個循環字節。 。這也解決了你可能會遇到的「部分接收」問題計數器。

+0

我發送的「長度」是一個整數,我編碼爲一個字符併發送。我使用atoi在接收端將其變回爲int。它正在被正確地發送和接收。 – natediggs

+1

@natediggs。當你recv的「長度」,你*總是*從套接字讀取四個字節,因爲'sizeof(int)== 4'(假設32位整數)。如果你「將它編碼爲字符」,情況如何,發送了多少字節? – Roddy

+0

添加了上面的代碼。 – natediggs

0

當您從套接字讀取數據時,您正在讀取數據流而不是文件。 請參閱here以獲得編寫網絡代碼的良好指導。

儘管發送端可能一次發送了所有數據,但並不保證您將一次看到所有數據。您必須在recv上循環,並考慮每次呼叫接收的字節數。如果recv返回0套接字已關閉或出現錯誤,您將不會收到來自該套接字的更多數據。

考慮到這些東西有點兒八九不離十僞代碼,我沒有測試這一點,但希望它給你的想法:

int expectedLength = readLengthFromSocket(socket, sizeof(int)); 
int bytesRead = 0; 
char buffer[expectedLength]; 
bytesRead = recv(socket, buffer, ...); 
runningLength = 0; 
if(bytesRead < 1) 
    // Socket closed or there was an error, handle that here 
else 
{ runningLength += bytesRead; 
    while(runningLength < expectedLength) 
    { 
     bytesRead = recv(socket, buffer + runningLength, ...); 
     if(bytesRead < 1) 
      // Socket closed or there was an error, handle that here 
      break; 
     else 
      runningLength += bytesRead; 
    } 
} 

注意,閱讀和寫作intlonglong longshort並且網絡上的未簽名變體通常需要字節交換。讀取一個字節的緩衝區不會。

有關說明,請參閱here