2015-06-16 33 views
0

我想實現一個簡單的文件傳輸。下面是我測試的兩種方法:發送文件在套接字編程tcp

方法一:發送和接收沒有拆分文件。 我硬編碼的文件大小,更容易測試。

發件人:

send(sock,buffer,107,NULL); //sends a file with 107 size 

接收機:

char * buffer = new char[107];      
recv(sock_CONNECTION,buffer,107,0); 

std::ofstream outfile (collector,std::ofstream::binary); 
outfile.write (buffer,107); 

輸出正如所料,該文件沒有被破壞,因爲.txt文件,我發送含有相同的內容作爲原始。

方法二:發送和由接收器上的側分裂內容接收。每個循環5個字節。

發件人:

send(sock,buffer,107,NULL); 

接收機:

char * buffer = new char[107];      //total file buffer 
char * ptr = new char[5];       //buffer 
int var = 5;     
int sizecpy = size; //orig size 

while(size > var){        //collect bytes 

    recv(sock_CONNECTION,ptr,5,0); 

    strcat(buffer,ptr);      //concatenate 
    size= size-var;  //decrease 
    std::cout<<"Transferring.."<<std::endl; 

    } 

    std::cout<<"did it reach here?"<<std::endl; 
    char*last = new char[size]; 

    recv(sock_CONNECTION,last,2,0); //last two bytes 
    strcat(buffer,last);  
    std::ofstream outfile (collector,std::ofstream::binary); 
    outfile.write (buffer,107); 

輸出:該文本文件包含無效字符特別是在開始和結束。

問題:我如何使方法2工作?大小是相同的,但他們產生不同的結果。方法2中原始文件與新文件的相似度約爲98〜99%,而方法一中爲100%。傳輸文件的最佳方法是什麼?

+0

您的網絡堆棧希望阻止您一次只通過線路發送5個字節。讚美Nagle! –

+0

我使用了5個字節進行更簡單的測試(因爲.txt文件尺寸較小,內容可以很容易比較),並知道我的方法是否可行。如果是這樣,那麼我打算增加它到1024或其他東西。 –

+2

'strcat'可能正在殺死你。你發送的內容不一定是NULL終止的,所以'strcat'不知道在哪裏結束。 – user4581301

回答

1

不同意Galik。最好不要使用strcat,strncat或除了預期的輸出緩衝區之外的任何東西。

TCP是knda樂趣。你永遠不知道你會得到多少數據,但你會得到它或錯誤。

這將一次讀取MAX字節。 #define MAX,無論你想要什麼。

std::unique_ptr<char[]> buffer (new char[size]); 
int loc = 0; // where in buffer to write the next batch of data 
int bytesread; //how much data was read? recv will return -1 on error       

while(size > MAX) 
{        //collect bytes 
    bytesread = recv(sock_CONNECTION,&buffer[loc],MAX,0); 
    if (bytesread < 0) 
    { 
     //handle error. 
    } 
    loc += bytesread; 
    size= size-bytesread;  //decrease 
    std::cout<<"Transferring.."<<std::endl; 
} 
bytesread = recv(sock_CONNECTION,&buffer[loc],size,0); 
if (bytesread < 0) 
{ 
    //handle error 
} 

std::ofstream outfile (collector,std::ofstream::binary); 
outfile.write (buffer.get(),size); 

更有趣的是,寫入輸出緩衝區,因此您不必存儲整個文件。在這種情況下MAX應該是一個更大的數字。

std::ofstream outfile (collector,std::ofstream::binary); 
char buffer[MAX]; 
int bytesread; //how much data was read? recv will return -1 on error       

while(size) 
{        //collect bytes 
    bytesread = recv(sock_CONNECTION,buffer,MAX>size?size:MAX,0); 
    // MAX>size?size:MAX is like a compact if-else: if (MAX>size){size}else{MAX} 
    if (bytesread < 0) 
    { 
     //handle error. 
    } 
    outfile.write (buffer,bytesread); 
    size -= bytesread;  //decrease 
    std::cout<<"Transferring.."<<std::endl; 
} 
+0

我在這一行上得到錯誤'outfile.write(buffer,size);'當我嘗試使用std :: unique_ptr buffer(new char [size]);' –

+0

謝謝。這教給我不要編譯。更正的例子,增加了更好的版本。 – user4581301

+0

方法二的問題在於,一旦文件低於最大值,讓我嘗試添加。 ty –

3

什麼是傳輸文件的最佳方法?

通常我不喜歡回答問題什麼是最好的方法。但在這種情況下,它是很明顯的:

  1. 您發送的文件大小和網絡字節序的校驗和,然後開始傳送
  2. 發送更多的報頭數據(如文件名)任選
  3. 客戶端讀取文件大小和校驗和,並將其解碼爲主機字節順序
  4. 您以合理大小的塊發送文件數據(5個字節不是合理的大小),塊應該匹配tcp/ip幀最大可用有效負載大小
  5. 您在客戶端按塊接收塊,直到先前發送的文件si澤匹配
  6. 您計算在客戶端接收數據的校驗,並檢查它是否匹配接收到一個beforhand

注:你並不需要結合在客戶端的所有內存塊中,只是將它們附加到存儲介質中的文件中。校驗和(CRC)通常也可以通過運行數據塊來計算。

+0

我所做的是..我首先發送包含文件名和文件大小的標題,然後我計劃循環直到滿足文件大小。 –

+0

o我也想問,我是否需要在客戶端分割文件大小?或者如果我將它們發送到1去就好了? –

+0

我想讓我的代碼更靈活,我想如果文件大小是'14000000'字節。 –

1

我看到的最初的問題是std::strcat。你不能在未初始化的緩衝區上使用它。此外,您不復制空終止的c字符串。您正在複製大小緩衝區。最好使用std::strncat爲:

char * buffer = new char[107];      //total file buffer 
char * ptr = new char[5];       //buffer 
int var = 5;     
int sizecpy = size; //orig size 

// initialize buffer 
*buffer = '\0'; // add null terminator 

while(size > var){        //collect bytes 

    recv(sock_CONNECTION,ptr,5,0); 

    strncat(buffer, ptr, 5); // strncat only 5 chars 

    size= size-var;  //decrease 
    std::cout<<"Transferring.."<<std::endl; 

} 

除此之外,你應該爲錯誤檢查所以插座庫可以告訴你,如果出了什麼差錯與通信。

+0

這個伎倆。謝謝。只有一個問題,這是否會變得靈活?假設病態增加文件大小。 –

+0

@CarloBrew靈活的方式?您顯然需要重新編寫它,以便您可以處理可變長度的文件。如果是我,我會使用'std :: vector '而不是原始分配的char數組。 – Galik