質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.47%

  • C++

    4522questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

winsockでのFTPファイル受信で失敗してしまう

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 363

moritaro

score 0

 前提・実現したいこと

WindowsPCからUnixPCに対してファイル送受信できるFTPクライアントソフトをC++で作成したい

 発生している問題・エラーメッセージ

当初、CFtpConnectionクラスを使用しアプリケーションを作成しました。
アプリケーションは完成したのですが、下記の条件が必要となりました。
1)ログイン失敗においても"QUIT"を送信しなければいけない
2)getコマンドでは"SIZE"を送信してはいけない
CFtpConnectionクラスを使用する場合"QUIT"はcommandメソッドを使用して送信していましたが、CInternetSessionクラスのGetFtpConnectionメソッドで失敗した場合はcommandメソッドを実行できない
CFtpConnectionクラスのGetFileメソッドではSIZEを送信してしまうことから、CFtpConnectionクラスを使用することは諦めました。

そこで、Winsockを使用しアプリケーションを作成しようと試みましたがファイルの受信でうまくいきません。

ファイル受信のところで
125 Data connection already open;Transfer starting. 226 Transfer complete
と返信されますが、ファイルは受信できていません。

質問1:ファイル受信の問題点を教えてください。
質問2:当初のCFtpConnectionクラスを使用した場合でも1)2)の条件を満たす手法はありますでしょうか。
宜しくお願いします。

 該当のソースコード

{
    // TODO: ここにコントロール通知ハンドラ コードを追加します。
    CString dir = "C:\\Test";
    if (_chdir(dir) != 0) return;


    char szStrRcv[1024];
    char szStr[256];

    WSADATA wsaData;

    WSAStartup(MAKEWORD(2, 0), &wsaData);

    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);

    //接続
    struct sockaddr_in adr;
    adr.sin_family = AF_INET;
    adr.sin_port = htons(21);
    adr.sin_addr.S_un.S_addr = inet_addr("192.168.200.2");

    if (connect(sock, (struct sockaddr*)&adr, sizeof(adr))) {
        closesocket(sock);
        WSACleanup();
        return;
    }

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "220", 3) != 0) {
        closesocket(sock);
        WSACleanup();
        return;
    }

    //コマンド
    CString user = "USER ftpuser\r\n";
    CString pass = "PASS ftppass\r\n";
    CString cwd = "CWD ./FTP\r\n";
    CString type = "TYPE A\r\n";
    CString get = "RETR FTP.TXT\r\n";

    //ユーザー
    strcpy_s(szStr, sizeof(szStr), user);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "331", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //パスワード
    strcpy_s(szStr, sizeof(szStr), pass);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "230", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //ディレクトリ移動
    strcpy_s(szStr, sizeof(szStr), cwd);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "250", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //アスキーモード
    strcpy_s(szStr, sizeof(szStr), type);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "200", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //バインド設定
    struct sockaddr_in mine;
    mine.sin_family = AF_INET;
    mine.sin_port = htons(0);
//    mine.sin_addr.s_addr = INADDR_ANY;
    int len = sizeof(sockaddr_in);
    if (getsockname(sock, (struct sockaddr*)&mine, &len) < 0) {
        goto CLOSE_QUIT;
    }
    unsigned long hi, low;
    hi = (ntohs(mine.sin_port) >> 8) & 0xff;
    low = ntohs(mine.sin_port) & 0xff;

    //ポート
    sprintf(szStr, "PORT %d,%d,%d,%d,%d,%d\r\n",
        mine.sin_addr.S_un.S_un_b.s_b1,
        mine.sin_addr.S_un.S_un_b.s_b2,
        mine.sin_addr.S_un.S_un_b.s_b3,
        mine.sin_addr.S_un.S_un_b.s_b4,hi, low);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "200", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //データコネクション
    SOCKET sockdata = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in adrdata;
    adrdata.sin_family = AF_INET;
    adrdata.sin_port = htons(hi * 256 + low);
//    adrdata.sin_addr.s_addr = INADDR_ANY;
    adrdata.sin_addr = mine.sin_addr;

    if (bind(sockdata, (struct sockaddr*)&adrdata, sizeof(adrdata)) == SOCKET_ERROR) {
        goto CLOSE_DATA;
    }

    if (listen(sockdata, SOMAXCONN) == SOCKET_ERROR) {
        goto CLOSE_DATA;
    }

    SOCKET AcceptSocket;
    AcceptSocket = accept(sockdata, NULL, NULL);
    if (AcceptSocket == INVALID_SOCKET) {
        goto CLOSE_DATA;
    }

    //ファイル受信
    strcpy_s(szStr, sizeof(szStr), get);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);
    if (strncmp(szStrRcv, "150", 3) != 0) {
        goto CLOSE_DATA;
    }

CLOSE_DATA:
    closesocket(sockdata);
CLOSE_QUIT:
    CString quit = "QUIT\r\n";
    strcpy_s(szStr, sizeof(szStr), quit);
    send(sock, szStr, (int)strlen(szStr), 0);

    closesocket(sock);
    WSACleanup();
}

 試したこと

 補足情報(FW/ツールのバージョンなど)

VisualStudio2005

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

check解決した方法

0

自己解決しました。

ファイルのデータを受信する必要がありました。
問題点がありまして、ファイル受信のところで
"125 Data connection alresdy open; Transfer starting"
"226 Transfer complete"
の2つのメッセージを受信してからデータを受信したいのですが、続けて上記2行を受信しようとすると226のところでrecv()すると返ってきません。(プログラムが進まない)
データ受信は別のスレッドで処理すればいいのでしょうか。

下記コードについて、改善点や不必要なコードなどあれば、ご指摘いただけますと助かります。

{
    // TODO: ここにコントロール通知ハンドラ コードを追加します。

    char szStrRcv[1024];
    char szStr[256];
    char acepStr[256];

    WSADATA wsaData;

    WSAStartup(MAKEWORD(2, 0), &wsaData);

    SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);

    //接続
    struct sockaddr_in adr;
    adr.sin_family = AF_INET;
    adr.sin_port = htons(21);
    adr.sin_addr.S_un.S_addr = inet_addr("192.168.200.2");

    if (connect(sock, (struct sockaddr*)&adr, sizeof(adr))) {
        closesocket(sock);
        WSACleanup();
        return;
    }

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "220", 3) != 0) {
        closesocket(sock);
        WSACleanup();
        return;
    }

    //コマンド
    CString user = "USER ftpuser\r\n";
    CString pass = "PASS ftppass\r\n";
    CString cwd = "CWD ./FTP\r\n";
    CString type = "TYPE A\r\n";
    CString get = "RETR FTP.TXT\r\n";

    FILE *fp;
    errno_t err;
    CString filename = "C:\\Test\\FTP.TXT";

    //ユーザー
    strcpy_s(szStr, sizeof(szStr), user);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "331", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //パスワード
    strcpy_s(szStr, sizeof(szStr), pass);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "230", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //ディレクトリ移動
    strcpy_s(szStr, sizeof(szStr), cwd);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "250", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //アスキーモード
    strcpy_s(szStr, sizeof(szStr), type);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "200", 3) != 0) {
        goto CLOSE_QUIT;
    }

    //バインド設定
    struct sockaddr_in mine;
    mine.sin_family = AF_INET;
    mine.sin_port = htons(0);
//    mine.sin_addr.s_addr = INADDR_ANY;
    int len = sizeof(sockaddr_in);
    if (getsockname(sock, (struct sockaddr*)&mine, &len) < 0) {
        goto CLOSE_QUIT;
    }
    unsigned long hi, low;
    hi = (ntohs(mine.sin_port) >> 8) & 0xff;
    low = ntohs(mine.sin_port) & 0xff;

    //データコネクション
    SOCKET sockdata = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in adrdata;
    adrdata.sin_family = AF_INET;
    adrdata.sin_port = htons(hi * 256 + low);
//    adrdata.sin_addr.s_addr = INADDR_ANY;
    adrdata.sin_addr = mine.sin_addr;

    if (bind(sockdata, (struct sockaddr*)&adrdata, sizeof(adrdata)) == SOCKET_ERROR) {
        goto CLOSE_DATA;
    }

    if (listen(sockdata, SOMAXCONN) == SOCKET_ERROR) {
        goto CLOSE_DATA;
    }

    //ポート
    sprintf(szStr, "PORT %d,%d,%d,%d,%d,%d\r\n",
        mine.sin_addr.S_un.S_un_b.s_b1,
        mine.sin_addr.S_un.S_un_b.s_b2,
        mine.sin_addr.S_un.S_un_b.s_b3,
        mine.sin_addr.S_un.S_un_b.s_b4,hi, low);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "200", 3) != 0) {
        goto CLOSE_DATA;
    }

    struct sockaddr_in adracep;
    int acep_len = sizeof(adracep);
    SOCKET AcceptSocket;
    AcceptSocket = accept(sockdata, (struct sockaddr*)&adracep, &acep_len);
    if (AcceptSocket == INVALID_SOCKET) {
        goto CLOSE_DATA;
    }

    //ファイル受信
    strcpy_s(szStr, sizeof(szStr), get);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "125", 3) != 0) {
        goto CLOSE_DATA;
    }

    memset(acepStr, '\0', sizeof(acepStr));
    int get_len;

    err = fopen_s(&fp, filename, "wb");
    int flen;
    if (err == 0) {
        do {
            get_len = recv(AcceptSocket, acepStr, sizeof(acepStr), 0);
            flen = fwrite(acepStr, sizeof(char), get_len, fp);
        } while (get_len == sizeof(acepStr));
    }
    if (fp) fclose(fp);
    closesocket(AcceptSocket);

    //ファイル大きいとファイル受信(RETR)の"125"の後のレシーブで返ってこない
    //よってファイル受信が完了していからレシーブする
    //これレシーブしないと"QUIT"の返信がとれない
    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "226", 3) != 0) {
        goto CLOSE_DATA;
    }

CLOSE_DATA:
    closesocket(sockdata);
CLOSE_QUIT:
    CString quit = "QUIT\r\n";
    strcpy_s(szStr, sizeof(szStr), quit);
    send(sock, szStr, (int)strlen(szStr), 0);

    memset(szStrRcv, '\0', sizeof(szStrRcv));
    recv(sock, szStrRcv, sizeof(szStrRcv)-1, 0);

    if (strncmp(szStrRcv, "221", 3) != 0) {
        //QUIT失敗時の処理
    }

    closesocket(sock);
    WSACleanup();
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.47%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る

  • C++

    4522questions

    C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。