我试图在C/C++中创建一个简单的FTP客户端,它将执行简单的操作(连接,检索文件)。到目前为止,我正在工作的是连接和登录。我使用套接字将端口连接到端口21,就像任何常规的FTP客户端一样。我遇到的麻烦是连接到输入命令PASV时指定的端口。我收到消息,将其解析,然后在输入PASV时根据重播消息计算端口。

227 Entering Passive Mode (a1, a2, a3, a4, p1, p2)
DataPort = (p1 * 256) + p2

有了端口后,我尝试创建另一个套接字并以相同的方式连接到该套接字。那就是我的问题所在。到目前为止,我的代码已发布在下面。我不知道是否需要以相同的方式再次获取服务器地址。我没有从服务器得到响应(如果我真的想得到一个响应,我不知道)。请提出任何问题或疑虑,谢谢。
const int FTP_PORT = 21;        // Server Port
const int SIZE = 1024;          // Size of Buffers
char receiveBuff[SIZE];         // Buffer to send to the server
char sendBuff[SIZE];            // Buffer to receive from server
char pasvBuff[] = "pasv";       // Buffer to see if PASV Command was entered
char quitBuff[] = "QUIT";       // Buffer to see if QUIT Command was entered
char pasvMessage[100];          // String for PASV information

int main(int argc, char *argv[])
{
    int length = 0, i=0;
    int a1, a2, a3, a4, p1, p2, dataPort;       //PASV Information

    /* Get Server Name from User */
    if (argc != 2)
    {
        cerr << "Usage: " << argv[0] << " server" << endl;
        return 1;
    }

    /* Obtain Host (Server) Info  */
    struct hostent *host;
    host = gethostbyname(argv[1]);
    if (host == (struct hostent *)NULL)
    {
        perror("Client: gethostbyname");
        return 2;
    }

    /* Add Server Information */
    struct sockaddr_in servAdr;             // Internet address of server
    memset(&servAdr, 0, sizeof(servAdr));   // Clear structure
    servAdr.sin_family = AF_INET;           // Set address typedef
    memcpy(&servAdr.sin_addr, host->h_addr, host->h_length);
    servAdr.sin_port = htons(FTP_PORT);     // Use FTP port

    /* Create Socket to Connect to FTP Server */
    int origSock;                   // Original socket in client
    if ((origSock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Client: generate error");
        return 3;
    }

    /* Connect to FTP Server on Port 21 */
    if (connect(origSock, (struct sockaddr *)&servAdr, sizeof(servAdr)) < 0)
    {
        perror("Client: connect error");
        return 4;
    }

    /* Get Conenct Message and Print to Screen */
    read(origSock, receiveBuff, sizeof(receiveBuff) - 1);
    write(fileno(stdout), receiveBuff, sizeof(receiveBuff) - 1);

    do
    {
        /* Clear Buffers */
        memset(receiveBuff, 0, SIZE);
        memset(sendBuff, 0, SIZE);

        write(fileno(stdout), "Please enter a FTP Command: ", 28);      // Write User Interface
        read(fileno(stdin), sendBuff, SIZE);                            // Read Command from User
        send(origSock, sendBuff, strlen(sendBuff) , 0);                 // Send Command to Server

        read(origSock, receiveBuff, sizeof(receiveBuff) - 1);           // Read Response from Server
        write(fileno(stdout), receiveBuff, sizeof(receiveBuff) - 1);    // Print Response from Server to screen

        /* If PASV Command was Entered */
        if (strncmp(sendBuff, pasvBuff, 4) == 0)
        {
            sscanf(receiveBuff, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", &a1,&a2,&a3,&a4,&p1,&p2);
            dataPort = (p1 * 256) + p2;

            struct sockaddr_in servAdr2;                // Internet address of server
            memset(&servAdr2, 0, sizeof(servAdr2));     // Clear structure
            servAdr2.sin_family = AF_INET;              // Set address typedef
            memcpy(&servAdr2.sin_addr, host->h_addr, host->h_length);
            servAdr.sin_port = htons(dataPort);         // Use FTP port

            /* Create Socket to Connect to FTP Server */
            int dataSock;                               // Data socket in client
            if ((dataSock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
            {
                perror("Client: generate error");
                return 3;
            }

            /* Connect to FTP Server on Data Port */
            if (connect(dataSock, (struct sockaddr *)&servAdr, sizeof(servAdr)) < 0)
            {
                perror("Client: connect error");
                return 4;
            }

            read(dataSock, receiveBuff, sizeof(receiveBuff) - 1);
            write(fileno(stdout), receiveBuff, sizeof(receiveBuff) - 1);
        }

    } while (strncmp(sendBuff, quitBuff, 4) != 0);      // Go until QUIT Command is entered

    close(origSock);

    return 0;
}

最佳答案

解析PASV答复时,将填充servAdr2变量,但其sin_port字段除外。您正在将报告的端口分配给servAdr.sin_port字段。然后,您将使用servAdr而不是servAdr2连接数据套接字。因此,您可以有效地将数据套接字连接到报告端口上服务器的原始IP地址,而不是连接到报告IP地址(可以与服务器IP不同)。 a1-a4是您应连接到的IP地址的IPv4八位字节。

就是说,如果服务器支持EPSV命令,则您实际上应该使用它。与PASV相比,解析起来要容易得多,因为PASV没有标准化的格式(因此准备解析多种特定于供应商的格式)。 EPSV通过以机器可解析的方式标准化格式来解决该问题。

至于为什么没有得到任何响应,这是因为您没有告诉服务器通过开放数据连接发送任何文件。发送PASV仅打开服务器的数据端口。连接到它之后,然后必须在控制套接字上发送STORRETR命令,以实际通过数据套接字执行文件传输。传输完成之后,您还必须在控制套接字上读取服务器的最终响应,然后才能发送任何新命令。

关于c++ - 简单的FTP客户端C++,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/33245774/

10-11 17:09