首先,我建议你停止使用Dev-C++.这实在是不合时宜,以及最新的测试版本是超过10年前出版:
February 21th 2005 : Dev-C++ 5 Beta 9.2 (4.9.9.2) released !
你应该从Cygwin尝试NetBeans C/C++捆绑和编译器/链接器(gcc/g ++/make)。有关如何设置此类开发环境的更多信息,请查看此链接here。你也可以尝试Eclipse CDT甚至Visual Studio Express或最新的Visual Studio Community。
现在为您的程序和代码。
尽管您的程序能够正常工作,但我发现了一些小错误,这些错误在编译并运行时无法正常工作。
您想要将文件上传到FTP服务器,并使用唯一的文件名将其保存到远程路径。
您可以简单地使用STOU
ftp命令而不是STOR
,ftp服务器将在当前工作目录下以唯一文件名保存该文件。因此,您必须更改sendFileRequest
过程才能使用CWD
将目录切换到我们要保存文件的目录,然后仅使用STOU
而不带任何参数,服务器将处理剩下的文件并使用唯一的文件名进行保存。例如,Pure-FTP服务器生成一个像这样的文件名pureftpd.583c3777.dd.0000
。
的sendFileRequest
过程会变成这个样子:
void sendFileRequest(SOCKET _FSoc){
char cwdCmd[MAX_PATH] = "CWD /web/share/tmp/\r\n";
send(_FSoc, cwdCmd, strlen(cwdCmd), 0);
Sleep(1000);
//char stor[] = "STOR /web/share/tmp/test.txt\r\n";
char stor[] = "STOU\r\n";
send(_FSoc, stor, strlen(stor), 0);
Sleep(1000);
}
当心,一些FTP服务器不支持STOU
命令。
现在,如果您想控制文件名并根据某些条件生成名称,则必须读取要上载文件的目录中的文件列表,然后生成一个唯一的文件名存在于远程目录的文件列表中。
我们想发出一个ftp命令来获取当前目录的文件名。 NLST
ftp命令是我们想要的,因为它发送包含文本数据的缓冲区,并为每个文件使用换行符分隔符;在当前工作目录中的所有文件/文件夹名称(只有文件名,没有更多数据)的列表。我们还需要使用被动模式打开数据连接来检索数据。另外我也注意到,尽管文件是使用远程服务器路径的唯一名称创建的,但根本没有传输数据。发生这种情况是因为我非常快地执行程序,并且之前的ftp会话没有用QUIT
命令正确关闭。当我在过程结束时添加QUIT
命令时,所有零数据文件传输问题都消失了。
代码级问题和言论
我已经改变TYPE I
图像/二进制TYPE A
文本,因为大多数的FTP服务器做修复从UNIX传送文本文件时,Windows格式,副行饲料CR LF
反之亦然。
您必须始终通过使用shutdown
和/或closesocket
关闭数据套接字连接来关闭数据连接,否则服务器可能会中止传输的数据。
你有一个功能转换到std::string
整数,但你也可以使用std::stoi
如果你告诉编译器使用-std=c++11
选项,使用C++ 11标准。
当您使用recv
读取服务器响应,你必须总是空使用recv
返回值终止响应缓冲区串,或仅处理字节recv
回报的多少。
该程序没有状态读取行为,这可能会导致很多错误和完全失败。正确的ftp应用程序行为必须始终取决于服务器发送到命令连接的响应。例如,如果ftp服务器达到了最大客户端限制,那么你的程序就不会知道,并且将继续发出命令直到它失败或结束。如果发生这种错误,程序不应该也不能继续。
编译器选项
-std=c++11
为std::stoi
连接器选项
-lws2_32
以包括用于winsock2.h
需要在cygwin
伪代码
- 初始化ftp server命令连接
- 发送FTP登录信息
- 生成唯一的文件名
- 读取远程目录文件名
- 生成随机的名字,直到我们不” t名称为文件列表
- 与唯一的文件名的文件发送到服务器
对于唯一的文件名,我只是用充满字符[A-Za-z0-0]
一个字符数组。您也可以选择,如果你会使用本地文件扩展名进行检查,并把它添加到唯一的文件名的末尾:
string generateUniqueFilename(SOCKET _FSoc,
int port,
string remotePath,
bool useLocalFileExtension,
string localFile)
例如,设置useLocalFileExtension
到true
当我们有一个本地.txt
文件,将结果具有.txt
分机O3xh8p939YN4hV.txt
的唯一文件名。
我的最终方案
#include <winsock2.h>
#include <windows.h>
#include <sstream>
#include <iostream>
#include <stdio.h>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <vector>
#define REMOTE_FTP_HOST_IP "x.x.x.x"
#define REMOTE_FTP_USERNAME "username"
#define REMOTE_FTP_PASSWORD "password"
#define ECHO_FTP_RESPONSE 1 // Set to 0 to disable ftp responses output
using namespace std;
// Declarations ---------------------------------------------------
void echoResponse(string);
void echoResponse(LPSTR);
void recvResponseAndEcho(SOCKET, LPSTR, int);
void recvResponseAndEcho(SOCKET);
string generateRandomFilename(int);
int readSocketData(int, LPSTR, int);
string generateUniqueFilename(SOCKET, int, string, bool, string);
void sendLogIn(SOCKET socket);
void sendFileRequest(SOCKET, string, string);
void sendTypeICmd(SOCKET);
void sendTypeACmd(SOCKET);
int sendPasvCmd(SOCKET);
void sendQuitCmd(SOCKET);
BOOL ftpSocket(string, int);
int sendFile(string, string);
// Program entry point --------------------------------------------
int main(){
sendFile("C:\\test.txt", "/remote/ftp/path/");
return EXIT_SUCCESS;
}
void echoResponse(string response) {
if(ECHO_FTP_RESPONSE) {
string respclean = response.erase(response.find_last_not_of(" \t\n\r") + 1);
cout << respclean << endl;
}
}
void echoResponse(LPSTR response) {
if(ECHO_FTP_RESPONSE) {
string respstr = response;
echoResponse(respstr);
}
}
void recvResponseAndEcho(SOCKET socket, LPSTR buffer, int bufsize) {
int nread = recv(socket, buffer, bufsize, 0);
*(buffer + nread) = 0;
echoResponse(buffer);
}
void recvResponseAndEcho(SOCKET socket) {
char srvResponse[4096];
recvResponseAndEcho(socket, (LPSTR)&srvResponse, sizeof(srvResponse));
}
// Generate o random filename using an array of charactres [A-Za-z0-9]
string generateRandomFilename(int length) {
string rndFilename;
char fileChars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"";
int fileCharsLength = strlen(fileChars);
srand(time(NULL));
for(int i = 0; i < length; i++)
rndFilename += fileChars[rand() % fileCharsLength];
return rndFilename;
}
int readSocketData(int port, LPSTR buffer, int buffer_size) {
SOCKET sock;
SOCKADDR_IN pasvserver;
int connectionerror2;
int trycount2 = 2;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET){
WSACleanup();
return 0;
}
pasvserver.sin_family = AF_INET;
pasvserver.sin_port = htons(port);
pasvserver.sin_addr.s_addr = inet_addr(REMOTE_FTP_HOST_IP);
connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
while(connectionerror2 == SOCKET_ERROR){
connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
trycount2++;
if(trycount2 = 10) {
closesocket(sock);
WSACleanup();
return 0;
}
}
int result = recv(sock, buffer, buffer_size, 0);
shutdown(sock, SD_BOTH);
closesocket(sock);
return result;
}
string generateUniqueFilename(SOCKET _FSoc, int port, string remotePath, bool useLocalFileExtension, string localFile) {
string newFilename;
char cwd[MAX_PATH];
char cmd[7] = "NLST\r\n";
char listFilesBuffer[4096];
char servermessage[2048];
int listFilesBufferReadLength;
strcpy(cwd, "CWD ");
strcat(cwd, remotePath.c_str());
strcat(cwd, "\r\n");
send(_FSoc, cwd, strlen(cwd), 0);
Sleep(1000);
recvResponseAndEcho(_FSoc, (LPSTR)&servermessage, sizeof(servermessage));
Sleep(1000);
send(_FSoc, cmd, strlen(cmd), 0);
listFilesBufferReadLength = readSocketData(port, listFilesBuffer, 4096);
if(listFilesBufferReadLength > 0) {
std::istringstream fileLines(listFilesBuffer);
string fileLine, localFileExtension;
std::vector<std::string> filesList;
while (std::getline(fileLines, fileLine)) {
// Trim whitespaces from each line
fileLine = fileLine.erase(fileLine.find_last_not_of(" \t\n\r") + 1);
if(fileLine != "." && fileLine != "..")
// Save to a vector or strings if it's not current and level up directory
filesList.push_back(fileLine);
}
if(useLocalFileExtension) {
int dotPos = localFile.find_last_of(".");
localFileExtension = dotPos > 0 ? localFile.substr(dotPos) : "";
}
vector<string>::iterator fileFind;
do {
// Generate random filenames of 14 characters length
// untill the filename does not exist on the remote FTP server path
newFilename = generateRandomFilename(14);
if(useLocalFileExtension)
newFilename += localFileExtension;
} while((fileFind = std::find(filesList.begin(), filesList.end(), newFilename)) != filesList.end());
}
return newFilename;
}
void sendLogIn(SOCKET socket) {
char userCmd[128] = "USER ";
char passCmd[128] = "PASS ";
strcat(userCmd, REMOTE_FTP_USERNAME);
strcat(userCmd, "\r\n");
send(socket, userCmd, strlen(userCmd), 0);
recvResponseAndEcho(socket);
strcat(passCmd, REMOTE_FTP_PASSWORD);
strcat(passCmd, "\r\n");
send(socket, passCmd, strlen(passCmd), 0);
recvResponseAndEcho(socket);
}
void sendFileRequest(SOCKET socket, string remoteDirectory, string storFilename) {
char cwdCmd[MAX_PATH] = "CWD ";
char storCmd[MAX_PATH] = "STOR ";
//char stouCmd[] = "STOU\r\n";
// Change current working directory
strcat(cwdCmd, remoteDirectory.c_str());
strcat(cwdCmd, "\r\n");
send(socket, cwdCmd, strlen(cwdCmd), 0);
Sleep(1000);
recvResponseAndEcho(socket);
// Send stor ftp command
strcat(storCmd, storFilename.c_str());
strcat(storCmd, "\r\n");
send(socket, storCmd, strlen(storCmd), 0);
// Send stou ftp command insted of stor
//send(socket, stouCmd, strlen(stouCmd), 0);
}
// Image/binary type
void sendTypeICmd(SOCKET socket) {
char typeCmd[] = "TYPE I\r\n";
send(socket, typeCmd, strlen(typeCmd), 0);
Sleep(1000);
recvResponseAndEcho(socket);
}
// Text type
void sendTypeACmd(SOCKET socket) {
char typeCmd[] = "TYPE A\r\n";
send(socket, typeCmd, strlen(typeCmd), 0);
Sleep(1000);
recvResponseAndEcho(socket);
}
int sendPasvCmd(SOCKET socket) {
int result = -1;
char pasvCmd[] = "PASV\r\n";
char srvResponse[4096];
send(socket, pasvCmd, strlen(pasvCmd), 0);
Sleep(1000);
recvResponseAndEcho(socket, (LPSTR)&srvResponse, sizeof(srvResponse));
// Read the pasv response, something like:
// 227 Entering Passive Mode (x,x,x,x,y,y)
// where x are the ip bytes and y are the port bytes
string pasvMessage = srvResponse;
int modeStart = pasvMessage.find("Mode");
if(modeStart > 0) {
int parenthesisStart = pasvMessage.find("(", modeStart);
if(parenthesisStart > 0) {
string currentPasvByte;
char currentChar;
int currentCharPos = parenthesisStart,
currentPasvByteIndex = 0,
highByte = -1,
lowByte = -1;
do {
currentChar = pasvMessage[++currentCharPos];
if(currentChar == ',' || currentChar == ')') {
if(currentPasvByteIndex == 4)
highByte = stoi(currentPasvByte);
else if(currentPasvByteIndex == 5)
lowByte = stoi(currentPasvByte);
currentPasvByteIndex++;
currentPasvByte = "";
} else
currentPasvByte += currentChar;
} while(currentChar != ')');
// Assemble the port number by joing hi and lo port number bytes
if(highByte != -1 && lowByte != -1)
result = (highByte << 8) | lowByte;
}
}
return result;
}
void sendQuitCmd(SOCKET socket) {
char quitCmd[] = "QUIT\r\n";
send(socket, quitCmd, strlen(quitCmd), 0);
recvResponseAndEcho(socket);
}
BOOL ftpSocket(string localFile, int port) {
BOOL result = false;
SOCKET sock;
SOCKADDR_IN pasvserver;
int connectionerror2;
int trycount2 = 2;
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock == INVALID_SOCKET){
WSACleanup();
return 0;
}
pasvserver.sin_family = AF_INET;
pasvserver.sin_port = htons(port);
pasvserver.sin_addr.s_addr = inet_addr(REMOTE_FTP_HOST_IP); //Once again the drivehq ftp server
connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
while(connectionerror2 == SOCKET_ERROR){
connectionerror2 = connect(sock, (LPSOCKADDR)&pasvserver, sizeof(struct sockaddr));
trycount2++;
if(trycount2 = 10) {
closesocket(sock);
WSACleanup();
return false;
}
}
HANDLE hFile = CreateFile(localFile.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);;
if(hFile != INVALID_HANDLE_VALUE) {
DWORD read;
char buffer[4096];
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
while(ReadFile(hFile, buffer, 4096, &read, NULL) && read > 0) {
send(sock, buffer, read, 0);
}
CloseHandle(hFile);
result = true;
}
shutdown(sock, SD_BOTH);
closesocket(sock);
return result;
}
int sendFile(string localFile, string remotePath) {
FreeConsole();
WSAData WData;
SOCKET FSoc;
SOCKADDR_IN server;
int connectionerror;
int trycount = 2;
int port;
WSAStartup(MAKEWORD(2,2), &WData);
FSoc = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(FSoc == INVALID_SOCKET){
WSACleanup();
return 0;
}
server.sin_family = AF_INET;
server.sin_port = htons(21);
server.sin_addr.s_addr = inet_addr(REMOTE_FTP_HOST_IP); //this is the drivehq ftp server address.
connectionerror = connect(FSoc, (LPSOCKADDR)&server, sizeof(struct sockaddr));
while(connectionerror == SOCKET_ERROR){
connectionerror = connect(FSoc, (LPSOCKADDR)&server, sizeof(struct sockaddr));
trycount++;
if(trycount = 10){
closesocket(FSoc);
WSACleanup();
return 0;
}
}
recvResponseAndEcho(FSoc);
sendLogIn(FSoc);
Sleep(1000); //give the server and the client sometime to deal with the influx of new messages
//so that data for the ip doesnt get mixed up.
// Send typea command to transfer text data.
// It works better for windows to unix text files transfer, bacause it fixes
// the line endnings CR, LF on most ftp servers
// Use typei command for binary data.
//sendTypeACmd(FSoc);
sendTypeACmd(FSoc);
// Open passing mode for the files list data
port = sendPasvCmd(FSoc);
// Generate a unique filename that does not exists on the FTP remote directory
string filename = generateUniqueFilename(FSoc, port, remotePath, true, localFile);
// Open passing mode for the file transfer data
port = sendPasvCmd(FSoc);
sendFileRequest(FSoc, remotePath, filename);
ftpSocket(localFile, port);
recvResponseAndEcho(FSoc);
sendQuitCmd(FSoc);
shutdown(FSoc, SD_BOTH);
closesocket(FSoc);
WSACleanup();
return 0;
}
粘贴代码时,请多加小心。但是,您应该注意[SO]不是代码写入服务。 –