首先,我建議你停止使用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]不是代碼寫入服務。 –