2012-07-30 78 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