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 不读取孔文件?

4

1 回答 1

2

此代码中存在各种逻辑漏洞和不正确/缺失的错误处理。一般来说,您确实需要清理此代码。

您将错误的sizeof()值传递给connect(),如果失败则无法正确处理错误connect()(您的重试循环无用)。您需要使用sizeof(sockaddr_in)orsizeof(passServer)代替sizeof(sockaddr). 您也没有passServer正确初始化。

您没有检查recv()错误。并且在recv()实际读取CHUCK_SIZE字节数的情况下,当您将空字节写入缓冲区(您不需要这样做)时,您会遇到缓冲区溢出,这会破坏内存,因为您将其写入超出了边界缓冲。

如果connect()失败,或者recv()由于服务器端发起的断开连接以外的任何错误而失败,您并没有告诉服务器中止传输。

一旦您告诉服务器进入被动模式,您需要连接到服务器告诉您的 IP/端口(不仅仅是端口),然后再发送您的RETR命令。

不要忘记向服务器发送TYPE命令,以便它知道发送文件字节的格式,例如TYPE AASCII 文本和TYPE I二进制数据。如果您尝试以错误的格式传输文件,可能会损坏数据。FTP 的默认值TYPE是 ASCII,而不是二进制。

最后,由于您显然不知道如何有效地编程套接字,我建议您使用 WinInet 库的 FTP 部分而不是直接使用 WinSock,例如FtpGetFile()函数。让 WinInet 为您处理传输 FTP 文件的细节。

于 2012-07-30T21:26:33.337 回答