我正在尝试使用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个字节。
为什么不读取孔文件?
答案 0 :(得分:2)
此代码中存在各种逻辑漏洞和错误/缺失错误处理。您通常需要清理此代码。
您将错误的sizeof()
值传递给connect()
,并且如果connect()
失败(您的重试循环无用),则无法正确处理错误。您需要使用sizeof(sockaddr_in)
或sizeof(passServer)
代替sizeof(sockaddr)
。您也没有正确初始化passServer
。
您没有检查recv()
是否有错误。并且在recv()
实际读取CHUCK_SIZE
个字节数的偶然情况下,您有一个缓冲区溢出,当您将空字节写入缓冲区(您不需要这样做)时会破坏内存因为你正在写它超过缓冲区的边界。
如果connect()
失败,或者recv()
因服务器端启动的断开连接之外的任何错误而失败,您就不会告诉服务器中止传输。
一旦告诉服务器进入被动模式,您需要先连接到服务器告诉您的IP /端口(不仅仅是端口),然后再发送RETR
命令。
不要忘记向服务器发送TYPE
命令,以便它知道发送文件字节的格式,例如TYPE A
表示ASCII文本,TYPE I
表示二进制数据。如果您尝试以错误的格式传输文件,则可能会损坏数据。 FTP的默认TYPE
是ASCII,而不是二进制。
最后,由于您似乎根本不知道如何有效地编写套接字,我建议您直接使用WinInet库的FTP部分而不是WinSock,例如FtpGetFile()
函数。让WinInet为您处理传输FTP文件的详细信息。