2012-01-11 113 views
2

我正在處理客戶端必須將文件上載到服務器的二進制文件傳輸程序。對於這種情況,我需要首先發送文件名,然後再發送文件內容。但這對我來說不可行。使用TCP通過套接字進行二進制文件傳輸

讓我們來看看代碼:

// Client-side code to send file name 
void sendFileName(
    int sd,   /*Socket Descriptor*/ 
    char *fname) /*Array Containing the file name */ 
{  
    int n , byteswritten=0 , written ; 
    char buffer[1024]; 
    strcpy(buffer , fname); 
    n=strlen(buffer); 
    while (byteswritten<n) 
    { 
     written=write(sd , buffer+byteswritten,(n-byteswritten)); 
     byteswritten+=written; 
    } 
    printf("File name : %s sent to server \n",buffer); 
} 

在這段代碼中,我通過套接字寫入文件名&服務器將讀取插槽它的名稱如下:

// Server-side code to read file name from client 
while ((n = read((int)connfd, (fname + pointer), 1024)) > 0) 
{ 
    pointer=pointer+n; 
} 

好,問題是我必須在發送文件名後在客戶端關閉寫入端,該文件名將成爲服務器端代碼的FIN段,以停止從服務器讀取數據。

如果我關閉讀端,如:

shutdown(sd,SHUT_WR);  //Closing write end at client side 

我怎麼能寫通過插座(即發送)的文件內容服務器,以便它可以從套接字讀取?

注意:我所做的就是在文件名後添加來自客戶端的文件內容,併爲內容添加特殊字符(用於通知文件名的末尾),然後添加文件內容。

在客戶端,

void readWriteFile(
    int sd,      /*Socket Descriptor */ 
    int fd,      /*File Descriptot */ 
    char *fname)     /*File Name */ 
{ 
    char buffer[1050]; 
    int n; 
    int len = 0; 
    char *tmp = (char *)malloc(sizeof (char) * (strlen(fname) + 2)); 
    strcpy(tmp, fname);   //Copying the file name with tmp 
    strcat(tmp, "/");   //Appending '/' to tmp 
    len = strlen(tmp); 
    memset(buffer, '\0', sizeof (buffer)); 
    strcpy(buffer, tmp);   //Now copying the tmp value to buffer 

    while ((n = read(fd, buffer + len, 1024)) > 0) 
    { 
     if (write(sd, buffer, n) != n) 
     { 
     printf("File write Error \n"); 
     exit(0); 
     } 
     len = 0; 
    } 
    printf("File sent successfully \n"); 
} 

而在服務器端,

char fname[50], buffer[1024]; 
    int n, fd; 
    int i; 
    int start = 0; 

    while ((n = read((int)connfd, buffer, 1024)) > 0) // Reading from socket 
    { 
     if (!start) 
     { 
      /* This 'if' loop will executed almost once i.e. until 
      getting the file name */ 
     for (i = 0; i < 1024; i++) 
     { 
      /* Since '/' is the termination character for file name */ 
      if (buffer[i] == '/') 
      { 
       start = 1;  // Got the file name 
       break; 
      } 

      fname[i] = buffer[i]; //Storing the file name in fname 
     } 

     fname[i] = '\0'; 

     /* Creating the file in the server side */ 
     fd = open(fname, O_WRONLY | O_CREAT, S_IRWXU); 

     if (fd < 0) 
     { 
      perror("File error"); 
      exit(0); 
     } 

     /* Here writing the buffer content to the file after 
      the (buffer+i+1), because after this address only, we 
      can get the original file content */ 

     write(fd, buffer + i + 1, n); 
     } 
     else 
     { 
     write(fd, buffer, n); 
     } 
    } 
    printf("%s received successful \n", fname); 

此代碼工作正常圖像,可執行&文本文件。但是如果我發送任何音頻文件,它不會在服務器端播放。尺寸保持不變。但我想知道爲什麼這會發生在音頻文件上。邏輯中有什麼錯誤?我還沒有嘗試過視頻文件。

+0

注意返回值給'written'可能是'在很普通的情況下-l',例如'errno'是'EGAIN'或'EINTR'。這會給你的邏輯帶來麻煩。 – 2012-01-11 05:00:31

+0

「但這對我來說不可行。」如果不可行,爲什麼要問這個問題呢?聽起來你已經放棄了。 – 2012-01-11 06:07:04

回答

8

爲什麼說「發送文件名後我必須在客戶端關閉寫入端」?你沒有完成發送你的數據,所以你肯定不想關閉套接字呢?

首先決定要如何組織你的數據,使接收器可以讀取它,並重建原始文件名和文件內容。你可能會想如何構建它的一些例子:

  • 發送文件名,用NULL字節終止,則發送內容。
  • 發送網絡字節順序(big endian)的包含了文件名的長度和文件內容的長度,分別兩個32位整數。然後發送文件名,然後發送文件內容。
  • 序列化兩者的文件名和文件內容轉換爲結構化文件形式,如JSON或XML。發送序列化版本。 (可能對您的使用案例過度)

無論您決定是否發送完整結構,請關閉套接字。

隨着這些安排的任何一個,接收器收到所有需要明確地重建原始文件的名稱和內容的信息。

UPDATE:你增加了更多你的問題,這幾乎是一個不同的問題...

你的服務器端代碼(一個讀取的文件名和文件內容)包含了很多錯誤。

  • 您認爲文件名將完全在第一批緩衝數據中傳遞。如果你在沒有找到文件名結束符的情況下耗盡了這個緩衝區,那麼你的代碼會遍佈整個地方(它會打開一個不正確的文件,它會寫入一些垃圾,它會返回到從下一個緩衝區讀取時開始再次查找文件名)。
  • 你寫了從數據到輸出文件的第一bufferful n字節,即使有不適n字節。實際上有n減去然而很多被使用的文件名。
  • 你不這樣做就read()write()任何錯誤檢查,但我會假設你省略了,爲了清楚的問題...
  • 你不檢查由另一端所提供的文件名是否超過你的50字節的緩衝區。

在另一個大小的代碼中,有一個明顯的緩衝區溢出,您可以將1024個字節讀入只有1024 - len個字節的緩衝區。

您需要修復這一切,然後才能指望任何工作。

相關問題