###前提・実現したいこと
FTPの処理手順を知るために、Winsockを利用してFTPを通してファイルをやり取りするソフトを作成しています。最終的にはWinsockを利用しないため(別のAPIを利用するため)、Winsockにある直接ファイルをやり取りするFTPGetFile/FTPPutFilといった関数は利用することはできません。send/recvを利用してデータを宗純したいと思います。
ネットからいろいろサンプルを使って作成していますが、PORTコマンドを実行した後、再度コネクト処理を実現するところで必ず失敗します。
手順が不足または間違っていると思いますが、よろしくご教授ください。
###発生している問題・エラーメッセージ
「対象のコンピューターによって拒否されたため、接続できませんでした。」
となり、2度目のCONNECT処理にて接続エラーになります。
なお、手動では問題なく接続できています。
手動での確認方法はコマンドプロンプトから"ftp -d"とftpのプロトコル表示をしながら確認しています。
###該当のソースコード
開発言語はVS2008上のC言語で行っています。
本ソースはPassiv接続をActive接続に書き直してテストしています。
void _tmain(void){ char szStr[256]; // 送信バッファ char szStrRcv[1024]; // 受信バッファ _tsetlocale(LC_ALL, _TEXT("")); // // FTPサーバーの定義 strcpy_s(::szFtpServer, sizeof(::szFtpServer), SERVER_NAME); strcpy_s(::szUserName, sizeof(::szUserName), USER_NAME); strcpy_s(::szPass, sizeof(::szPass), PASSWORD); WSADATA wsaData; LPHOSTENT lpHost; SOCKET s; SOCKADDR_IN sockadd; // Winsockの初期化 if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { WSA_ERROR_MSG(_TEXT("関数WSAStartup:error")); return; } lpHost = gethostbyname(::szFtpServer); if (lpHost == NULL) { WSA_ERROR_MSG(_TEXT("関数gethostbyname:error")); return; } // コントロールコネクション用のソケット作成 s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { WSA_ERROR_MSG(_TEXT("関数socket:error")); WSACleanup(); return; } sockadd.sin_family = AF_INET; sockadd.sin_port = htons(PORT); // ホストバイトオーダーをネットワークバイトオーダーに変換 sockadd.sin_addr.S_un.S_addr = inet_addr(::szFtpServer); // サーバーのIPアドレス取得 if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd))) { // コントロールコネクションサーバーへ接続 WSA_ERROR_MSG(_TEXT("関数connect:error")); closesocket(s); WSACleanup(); return; } memset(szStrRcv, '\0', sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv)-1, 0); // サーバーからのメッセージを取得 printf(szStrRcv) ; if (strncmp(szStrRcv, "220", 3) != 0){ WSA_ERROR_MSG(_TEXT("error")); closesocket(s); WSACleanup(); return; } // ユーザー名を送信 sprintf_s(szStr, sizeof(szStr), "USER %s\r\n", ::szUserName); printf(szStr) ; send(s, szStr, (int)strlen(szStr), 0); memset(szStrRcv, 0, sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv)-1, 0); printf(szStrRcv) ; if (strncmp(szStrRcv, "331", 3) != 0){ WSA_ERROR_MSG(_TEXT("error")); closesocket(s); WSACleanup(); return; } // パスワードを送信 sprintf_s(szStr, sizeof(szStr), "PASS %s\r\n", ::szPass); printf(szStr) ; send(s, szStr, (int)strlen(szStr), 0); memset(szStrRcv, 0, sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv)-1, 0); printf(szStrRcv) ; if (strncmp(szStrRcv, "230", 3) != 0){ WSA_ERROR_MSG(_TEXT("error")); closesocket(s); WSACleanup(); return; } // サーバーのカレントディレクトリを指定 sprintf_s(szStr, sizeof(szStr), "CWD ./\r\n"); printf(szStr) ; send(s, szStr, (int)strlen(szStr), 0); memset(szStrRcv, 0, sizeof(szStrRcv)); recv(s, szStrRcv, sizeof(szStrRcv)-1, 0); printf(szStrRcv) ; //BIND設定 SOCKADDR_IN sockmine; sockmine.sin_family = AF_INET; int len = sizeof(SOCKADDR_IN); //sockmine.sin_addr.s_addr = INADDR_ANY; //sockmine.sin_port = 0; if (getsockname(s, (struct sockaddr *)&sockmine, &len) < 0) { WSA_ERROR_MSG(_TEXT("getsockname failed.\n")); closesocket(s); WSACleanup(); return; } unsigned long low, hi; hi = (ntohs(sockmine.sin_port) >> 8) & 0xff; low = ntohs(sockmine.sin_port) & 0xff; sprintf_s(szStr, "PORT %d,%d,%d,%d,%d,%d\n", sockmine.sin_addr.S_un.S_un_b.s_b1, sockmine.sin_addr.S_un.S_un_b.s_b2, sockmine.sin_addr.S_un.S_un_b.s_b3, sockmine.sin_addr.S_un.S_un_b.s_b4, hi,low); send(s, szStr, (int)strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStr)); recv(s, szStrRcv, sizeof(szStrRcv) - 1, 0); printf(szStrRcv) ; sockmine.sin_addr.S_un.S_addr = inet_addr(::szFtpServer) ; SOCKET s2 = FtpDataConnect(sockmine.sin_addr, hi * 256 + low); // LISTコマンドを送信 sprintf_s(szStr, sizeof(szStr), "LIST\r\n"); printf(szStr) ; send(s, szStr, (int)strlen(szStr), 0); memset(szStrRcv, '\0', sizeof(szStr)); recv(s, szStrRcv, sizeof(szStrRcv)-1, 0); printf(szStrRcv) ; // データーコネクションサーバーからLISTコマンドの結果を受け取る FtpList(s2); closesocket(s2); closesocket(s); WSACleanup(); return; } // データーコネクションサーバーへ接続 SOCKET FtpDataConnect(in_addr ipa, unsigned int port){ SOCKET s; s = socket(AF_INET, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { WSA_ERROR_MSG(_TEXT("関数socket:error")); return 0; } struct sockaddr_in sockadd = { AF_INET, htons(port) }; sockadd.sin_addr = ipa; if (connect(s, (PSOCKADDR)&sockadd, sizeof(sockadd)) == -1) { WSA_ERROR_MSG(_TEXT("関数connect:error")); closesocket(s); return 0; } return s; } // LISTコマンドの結果をファイルftplist.txtに保存 void FtpList(SOCKET s){ char buf[16]; FILE* fp; if (_tfopen_s(&fp, _TEXT("D:\\ftplist.txt"), _TEXT("w") )){ _tprintf(TEXT("ファイルが開けません\n")); return; } int len; do{ len = recv(s, buf, sizeof(buf), 0); fwrite(buf, sizeof(char), len, fp); } while (len == sizeof(buf)); fclose(fp); } // Winsockのエラーコードに対応するメッセージを標準出力に表示する void WSA_erro_msg(void){ __WSA_ERROR_CODE__ = WSAGetLastError(); LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, __WSA_ERROR_CODE__, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // 既定の言語 (TCHAR*)&lpMsgBuf, 0, NULL ); _tprintf(_TEXT("%s\n"), lpMsgBuf); // バッファを解放する。 LocalFree(lpMsgBuf); }
###試したこと
ポート番号が手動でコマンド打ちした場合(コマンドラインから"ftp -d"で動作)と、ポート番号があっていないのが原因かと思い、ポート番号を固定してみましたが、変化ありませんでした。
###補足情報(言語/FW/ツール等のバージョンなど)
VS2013/2015上でもコンパイル動作はしていますので、VSのバージョンの問題はありません。
回答3件
あなたの回答
tips
プレビュー