2012-07-30 66 views
1

我試圖使用winsock實現一個簡單的FTP客戶端。我在嘗試下載文件時遇到問題。下面是我使用的時刻代碼:recv()只讀取1個字節(使用winsock實現FTP)

bool FTPHandler::downloadFile(const char * remoteFilePath, const char * filePath) { 
    if (!isConnected()) { 
     setErrorMsg("Not connected, imposible to upload file..."); 
     return false; 
    } 

    if (usePasiveMode) { 
     this->pasivePort = makeConectionPasive(); 
     if (this->pasivePort == -1) { 
      //error msg will be setted by makeConectionPasive() 
      return false; 
     } 
    } else { 
     setErrorMsg("Unable to upload file not in pasive mode :S"); 
     return false; 
    } 

    char * fileName = new char[500]; 
    getFileName(remoteFilePath,fileName);  

    // Default name and path := current directory and same name as remote. 
    if (filePath == NULL) { 
      filePath = fileName; 
    } 

    if (!setDirectory(remoteFilePath)) { 
     return false; 
    } 



    char msg[OTHER_BUF_SIZE]; 
    char serverMsg[SERVER_BUF_SIZE]; 
    sprintf(msg,"%s%s\n",RETR_MSG,fileName); 
    send(sock, msg, strlen(msg), 0); 

    SOCKET passSocket; 
    SOCKADDR_IN passServer; 

    passSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 
    if (passSocket == INVALID_SOCKET) { 
     WSACleanup(); 
     sprintf(errorMsg,"Error trying to create socket (WSA error code: %d)",WSAGetLastError()); 
     return false; 
    } 

    passServer.sin_family = PF_INET; 
    passServer.sin_port = htons(this->pasivePort); 
    passServer.sin_addr = *((struct in_addr *)gethostbyname(this->host)->h_addr); 
    memset(server.sin_zero,0,8); 

    int errorCode = connect(passSocket, (LPSOCKADDR) &passServer, sizeof(struct sockaddr)); 
    int tries = 0; 
    while (errorCode == SOCKET_ERROR) { 
      tries++; 
      if (tries >= MAX_TRIES) { 
       closesocket(passSocket); 
       sprintf(errorMsg,"Error trying to create socket"); 
       WSACleanup(); 
       return false; 
      } 
    } 

    char * buffer = (char *) malloc(CHUNK_SIZE); 
    ofstream f(filePath); 

    Sleep(WAIT_TIME); 
    while (int readBytes = ***recv(passSocket, buffer, CHUNK_SIZE, 0)***>0) { 
      buffer[readBytes] = '\0'; 
      f.write(buffer,readBytes); 
    } 
    f.close(); 

    Sleep(WAIT_TIME); 
    recv(sock, serverMsg, OTHER_BUF_SIZE, 0); 
    if (!startWith(serverMsg, FILE_STATUS_OKEY_CODE)) { 
     sprintf(errorMsg,"Bad response: %s",serverMsg); 
     return false; 
    } 

    return true; 
} 

這最後的recv()返回1個字節幾次,然後該方法結束這應該是各地的1Kb的文件僅僅是23個字節。

爲什麼不recv讀洞文件?

+0

什麼是23字節?另外,在打開與服務器的數據連接之前,我並不確定是否發佈了RETR。數據端口被打開後,服務器是否應該發送一些ACK消息,也就是說,客戶端應該在發佈RETR之前收聽ACK? – 2012-07-30 17:36:00

+0

這23個字節看起來很像。如果我嘗試下載一個小文件(如50字節),那麼它只能讀取1個字節。我不認爲服務器應該發送任何ACK消息。 – 2012-07-30 18:08:01

+0

好的,也許你是對的 - 自從我做這些東西以來,有一段時間了。接下來,'buffer [readBytes] ='\ 0'是什麼';'做?它似乎在數據的末尾放置了空值,然後不會寫入文件,所以它是多餘的。接下來,調試 - 到目前爲止你做了什麼?如果您在f.write中斷,readBytes的值和緩衝區中的值是什麼? – 2012-07-30 19:18:01

回答

2

在這段代碼中有各種邏輯漏洞和不正確的/錯誤的錯誤處理。一般來說,你真的需要清理這些代碼。

如果connect()失敗(您的重試循環無用),您將錯誤的sizeof()值傳遞給connect(),並且不能正確處理錯誤。您需要使用sizeof(sockaddr_in)sizeof(passServer)而不是sizeof(sockaddr)。你也沒有正確初始化passServer

您沒有檢查recv()是否有錯誤。並且在recv()實際上讀取字節數的機會,然後你有一個緩衝區溢出,當你寫空字節到緩衝區(你不需要做)因爲你寫它通過邊界的緩衝區。

如果connect()失敗,或者recv()因服務器端發起的斷開連接而發生任何錯誤而失敗,則說明您沒有通知服務器中止傳輸。

一旦您告訴服務器進入被動模式,您需要連接到服務器告訴您的IP /端口(不僅僅是端口),然後再發送您的RETR命令。

不要忘記發送服務器一個TYPE命令,以便知道以什麼格式發送文件字節,例如對於ASCII文本爲TYPE A,對於二進制數據爲TYPE I。如果您嘗試以錯誤的格式傳輸文件,則可能會破壞數據。 FTP的默認TYPE是ASCII,而不是二進制。最後,因爲你明顯不知道如何有效地編程套接字,所以我建議你直接使用WinInet庫的FTP部分而不是WinSock,例如FtpGetFile()函數。讓WinInet處理傳輸FTP文件的細節。

+0

感謝所有的評論,當我不得不時,我忘了接受答案。我不得不說,所有這些都是爲了更好地理解FTP的工作方式,而不是下載文件=) – 2014-10-09 11:05:35