Linux/Unix 系统编程 — FTP客户端

尊重作者劳动成果,转载请注明出处,谢谢!

1. ftp.h

#ifndef ftp_H
#define ftp_H

#include "types.h"
#include "socket.h"

#ifdef __cplusplus
extern "C"
{
#endif
    int ftp_login(const char *servIp, unsigned short port, const char *user, const char *password);
    boolean ftp_quit(int sockfd);
    boolean ftp_getCurrentDirectory(int sockfd, char *directory);
    boolean ftp_changDirectory(int sockfd, const char *directory);
    boolean ftp_changDirectoryUp(int sockfd);
    boolean ftp_createDirectory(int sockfd, const char *directory);
    boolean ftp_fileList(int sockfd);
    boolean ftp_download(int sockfd, const char *remoteFileName, const char *filePath);
    boolean ftp_upload(int sockfd, const char *remoteFileName, const char *filePath);
#ifdef __cplusplus
}
#endif

#endif

2. ftp.c

#include "ftp.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

static unsigned short strToHostAndPort(const char *str, char *host)
{
    int addr[6];
    sscanf(str, "%*[^(](%d,%d,%d,%d,%d,%d)", &addr[0], &addr[1], &addr[2], &addr[3], &addr[4], &addr[5]);

    bzero(host, strlen(host));
    sprintf(host, "%d.%d.%d.%d", addr[0], addr[1], addr[2], addr[3]);

    unsigned short port;
    port = addr[4] * 256 + addr[5];
    return port;
}

static boolean recvAndCheck(int sockfd, const char *code)
{
    char buf[1024] = {0};
    int nrecv = recv(sockfd, buf, 1024, 0);
    if (nrecv <= 0)
        return False;

    printf("%s\n", buf);
    return strncmp(buf, code, 3) == 0 ? True : False;
}

static boolean ftp_sendCommandWithResult(int sockfd, const char *cmd, const char *arg, const char *code, char *result)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    char *cmdBuf = (char *)malloc(strlen(cmd) + strlen(arg) + 10);
    sprintf(cmdBuf, "%s %s\r\n", cmd, arg);
    int cmdBufSize = strlen(cmdBuf);

    int nsend = send(sockfd, cmdBuf, cmdBufSize, 0);
    free(cmdBuf);
    if (nsend != cmdBufSize)
        return False;

    char buf[1024] = {0};
    int nrecv = recv(sockfd, buf, 1024, 0);
    printf("%s\n", buf);
    if (nrecv <= 0)
        return False;

    if (result != NULL)
        strcpy(result, buf);

    return strncmp(buf, code, 3) == 0 ? True : False;
}

static boolean ftp_sendCommand(int sockfd, const char *cmd, const char *arg, const char *code)
{
    return ftp_sendCommandWithResult(sockfd, cmd, arg, code, NULL);
}

//使用被动模式,并返回数据端口的文件描述符
static int ftp_uesPasvMode(int sockfd)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    char buf[1024] = {0};

    //227 Entering Passive Mode (192,168,1,100,191,160).
    if (!ftp_sendCommandWithResult(sockfd, "PASV", "", "227", buf))
        return False;

    char data_host[32] = {0};
    unsigned short data_port;
    data_port = strToHostAndPort(buf, data_host);

    return createTcpClient(data_host, data_port);
}

//ftp登录
int ftp_login(const char *servIp, unsigned short port, const char *user, const char *password)
{
    int sockfd = createTcpClient(servIp, port);
    if (sockfd == INVALID_SOCKET)
        return -1;

    //220 (vsFTPd 3.0.3)
    if (!recvAndCheck(sockfd, "220"))
    {
        close(sockfd);
        return INVALID_SOCKET;
    }

    //331 Please specify the password.
    if (!ftp_sendCommand(sockfd, "USER", user, "331"))
    {
        close(sockfd);
        return INVALID_SOCKET;
    }

    //230 Login successful.
    if (!ftp_sendCommand(sockfd, "PASS", password, "230"))
    {
        close(sockfd);
        return INVALID_SOCKET;
    }

    //200 Switching to Binary mode.
    if (!ftp_sendCommand(sockfd, "TYPE", "I", "200"))
    {
        close(sockfd);
        return INVALID_SOCKET;
    }

    return sockfd;
}

//ftp注销
boolean ftp_quit(int sockfd)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    //221 Goodbye.
    if (!ftp_sendCommand(sockfd, "QUIT", "", "221"))
        return False;

    close(sockfd);
    return True;
}

//ftp获取当前工作目录
boolean ftp_getCurrentDirectory(int sockfd, char *directory)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    //257 "/root" is the current directory
    char buf[1024];
    if (!ftp_sendCommandWithResult(sockfd, "PWD", "", "257", buf))
        return False;

    int i = 0;
    char *ptr = buf + 5;
    while (*ptr != '"')
    {
        directory[i++] = *ptr;
        ptr++;
    }

    directory[i] = '\0';
    return True;
}

//ftp更改当前工作目录
boolean ftp_changDirectory(int sockfd, const char *directory)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    //250 Directory successfully changed.
    return ftp_sendCommand(sockfd, "CWD", directory, "250");
}

//ftp更改当前工作目录为上一级目录
boolean ftp_changDirectoryUp(int sockfd)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    //250 Directory successfully changed.
    return ftp_sendCommand(sockfd, "CDUP", "", "250");
}

//ftp创建文件夹
boolean ftp_createDirectory(int sockfd, const char *directory)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    //257 "directory" created
    return ftp_sendCommand(sockfd, "MKD", directory, "257");
}

//ftp文件列表
boolean ftp_fileList(int sockfd)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    int data_fd = ftp_uesPasvMode(sockfd);
    if (data_fd == INVALID_SOCKET)
        return False;

    //150 Here comes the directory listing.
    if (!ftp_sendCommand(sockfd, "LIST", "", "150"))
    {
        close(data_fd);
        return False;
    }

    int bufSize = 1024;
    char buf[1024] = {0};
    int nrecv;
    while ((nrecv = recv(data_fd, buf, bufSize, 0)) > 0)
    {
        //drwx------    2 0        0               0 Sep 24 05:51 123
        //-rwx------    1 0        0           34980 Aug 20 04:28 devserv
        //-rw-------    1 0        0              30 Aug 20 03:58 devserv.conf
        printf("%s\n", buf);
    }

    close(data_fd);

    //226 Directory send OK.
    return recvAndCheck(sockfd, "226");
}

//ftp下载文件
boolean ftp_download(int sockfd, const char *remoteFileName, const char *filePath)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    int data_fd = ftp_uesPasvMode(sockfd);
    if (data_fd == INVALID_SOCKET)
        return False;

    //150 Opening BINARY mode data connection for "remoteFileName" (n bytes)
    if (!ftp_sendCommand(sockfd, "RETR", remoteFileName, "150"))
    {
        close(data_fd);
        return False;
    }

    int file_fd = open(filePath, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
    if (file_fd < 0)
    {
        close(data_fd);
        return False;
    }

    int bufSize = 4096;
    char buf[4096];
    int nread;
    while ((nread = recv(data_fd, buf, bufSize, 0)) > 0)
    {
        write(file_fd, buf, nread);
    }

    close(file_fd);
    close(data_fd);

    //226 Transfer complete.
    return recvAndCheck(sockfd, "226");
}

//ftp上传文件
boolean ftp_upload(int sockfd, const char *remoteFileName, const char *filePath)
{
    if (sockfd == INVALID_SOCKET)
        return False;

    if (access(filePath, F_OK) != 0)
        return False;

    int data_fd = ftp_uesPasvMode(sockfd);
    if (data_fd == INVALID_SOCKET)
        return False;

    //150 Ok to send data.
    if (!ftp_sendCommand(sockfd, "STOR", remoteFileName, "150"))
    {
        close(data_fd);
        return False;
    }

    int file_fd = open(filePath, O_RDONLY);
    if (file_fd < 0)
    {
        close(data_fd);
        return False;
    }

    int bufSize = 4096;
    char buf[4096];
    int nread;
    while ((nread = read(file_fd, buf, bufSize)) > 0)
    {
        send(data_fd, buf, nread, 0);
    }

    close(file_fd);
    close(data_fd);

    //226 Transfer complete
    return recvAndCheck(sockfd, "226");
}

 

上一篇:tcp服务器和客户端代码实现


下一篇:从入门到入土:基于C语言采用UDP协议实现通信功能的程序