设为首页 收藏本站
查看: 1065|回复: 0

写了个Linux下简单的FTP客户端程序

[复制链接]

尚未签到

发表于 2015-5-27 09:01:20 | 显示全部楼层 |阅读模式
  上一个项目写了个POS机下面的广告程序,其中需要先写一个POS机下的FTP客户端程序,这个程序很让我郁闷。首先,对方公司提供了一个FTP操作的中间层,所以与其说写,不如说是组合代码;其次,让我不爽的是,对方中间层的API有误,他们还叫我跟着他们瞎折腾测试这中间层的功能。总之就是很不爽,因为你明明知道他们是在瞎折腾,但是你又不得不配合他们,不配合他们,他们就有理由说我不合作,项目拖延是我的错,我很不爽。
  这一次我自己在Linux下写了一个简单的FTP客户端,支持的操作只有那些最常用的,以后如果有必要,这个程序可以移植到POS机下面。这个程序已经通过简单的一些测试,目前实现的操作有:ls, dir, pwd, cd, ascii, binary, passive, get, put, delete, system, mkdir, rmdir, quit, bye. 另外,我用的了steven那本《UNIX网络编程卷1》的动态库,因为里面包含了一些API的封装,我无须理会这些API的返回码,所以程序需要包含unp.h这个头文件和链接libunp.so这个动态库。
  这个FTP客户端程序主要分两个模块,一个是ftp_socket.c,负责socket方面的操作,另外一个是ftp.c,负责FTP的操作实现。有参考了网上开源的项目中PORT和PASV部分的处理,其他其实都挺简单的。核心代码不到900行,其中有一些地方没考虑得很全面,一些处理得不够优雅,以后慢慢再修改,在git上已经给程序打了一个tag。直接贴代码了:
  typedef.h : 声明和定义了一些类型和操作


DSC0000.gif DSC0001.gif 代码



#ifndef TYPEDEF_H
#define TYPEEF_H
typedef enum _Ret
{
    FTP_RET_OK,
    FTP_RET_OOM,
    FTP_RET_STOP,
    FTP_RET_INVALID_PARAMS,
    FTP_RET_FAIL
} FTP_Ret;
#define DECLS_BEGIN
#define DECLS_END
#define and &&
#define or  ||
#define return_if_fail(p)    if (!(p))        \
    {printf("%s:%d Warning: "#p" failed.\n",\
            __func__, __LINE__); return; }
#define return_val_if_fail(p, ret)    if (!(p))    \
    {printf("%s:%d Warning: "#p" failed.\n",    \
            __func__, __LINE__); return (ret); }
#define SAFE_FREE(p)    if (p != NULL) {free(p); p = NULL;}
#endif

  ftp_socket.






#include "typedef.h"
#ifndef FTP_SOCKET_H
#define FTP_SOCKET_H
DECLS_BEGIN
#define INVALID_SOCKET  (~0)
#define FD_READ_BIT      0
#define FD_READ          (1 transMode      = FTP_MODE_PASV;
    memset(ftpObj->replyString, 0x00, sizeof(ftpObj->replyString));
    return FTP_RET_OK;
}
static FTP_Ret ftp_get_reply(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(ftpObj->commandChannel != INVALID_SOCKET, FTP_RET_FAIL);
    char recvBuf[MAX_RECV_SIZE] = {0x00};
    ftpObj->replyCode = -1;
    memset(ftpObj->replyString, 0x00, sizeof(ftpObj->replyString));
    while (ftp_socket_select(ftpObj->commandChannel, FD_READ, ftpObj->secTimeOut) > 0)
    {
        ftp_set_nonblock(ftpObj->commandChannel);
        if (ftp_recv_command(ftpObj->commandChannel, recvBuf, MAX_RECV_SIZE) replyString, recvBuf, strlen(recvBuf));
            return FTP_RET_OK;
        }
    }
    return FTP_RET_FAIL;
}
static FTP_Ret ftp_send_and_recv_reply(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    ftp_send_command(ftpObj->commandChannel, command);
    memset(ftpObj->replyString, 0x00, sizeof(ftpObj->replyString));
    return ftp_get_reply(ftpObj);
}
static FTP_Obj *ftp_connect_server(const char *ipAddress, const int port)
{
    return_val_if_fail(ipAddress != NULL, NULL);
    FTP_Obj *ftpObj = (FTP_Obj *)malloc(sizeof(FTP_Obj));
    return_val_if_fail(ftpObj != NULL, NULL);
    ftp_init_obj(ftpObj);
    ftpObj->commandChannel = ftp_socket_create();
    if (ftpObj->commandChannel == INVALID_SOCKET)
    {
        fprintf(stderr, "creating socket failed.\n");
        goto error;
    }
    else
    {
        int ret = ftp_socket_connect(ftpObj->commandChannel, ipAddress, port);
        if (ret != 0)
        {
            fprintf(stderr, "connect to %s failed.\n", ipAddress);
            goto error;
        }
        else
        {
            fprintf(stdout, "connected to %s.\n", ipAddress);
            if (ftp_get_reply(ftpObj) == FTP_RET_OK)
            {
                return ftpObj;
            }
            else
            {
                goto error;
            }
        }
    }
error:
    SAFE_FREE(ftpObj);
    return NULL;
}
static void ftp_login_enter_name(char *userName, const int maxSize, const char *ipAddress)
{
    char enterNamePrompt[MAX_PROMPT_SIZE] = {0x00};
    sprintf(enterNamePrompt, "Name (%s): ", ipAddress);
    printf("%s", enterNamePrompt);
    Fgets(userName, maxSize, stdin);
    userName[strlen(userName) - 1] = '\0';
}
static void ftp_login_enter_password(char *userPassword, const int maxSize)
{
    char enterPwPrompt[MAX_PROMPT_SIZE] = {0x00};
    sprintf(enterPwPrompt, "Password: ");
    printf("%s", enterPwPrompt);
    Fgets(userPassword, maxSize, stdin);
    userPassword[strlen(userPassword) - 1] = '\0';
}
static FTP_Ret ftp_login(FTP_Obj *ftpObj, const char *ipAddress)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(ipAddress != NULL, FTP_RET_INVALID_PARAMS);
    char command[COMMAND_SIZE];
    char userName[20] = {0x00};
    char userPassword[20] = {0x00};
    ftp_login_enter_name(userName, 20, ipAddress);
    memset(command, 0x00, sizeof(command));
    sprintf(command, "USER %s", userName);
    if (ftp_send_and_recv_reply(ftpObj, command) == FTP_RET_OK)
    {
        if (strncmp(ftpObj->replyString, "331", 3) != 0)
        {
            return FTP_RET_FAIL;
        }
        ftp_login_enter_password(userPassword, 20);
        memset(command, 0x00, sizeof(command));
        sprintf(command, "PASS %s", userPassword);
        if (ftp_send_and_recv_reply(ftpObj, command) == FTP_RET_OK)
        {
            if (strncmp(ftpObj->replyString, "230", 3) != 0)
            {
                return FTP_RET_FAIL;
            }
        }
    }
    else
    {
        return FTP_RET_FAIL;
    }
    return FTP_RET_OK;
}
static FTP_Ret ftp_syst(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, "SYST");
}
static FTP_Ret ftp_set_transfer_type(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}
static FTP_Ret ftp_set_transfer_mode(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}
static bool ftp_pasv(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);
    char pasv[32] = {0x00};
    char hostAddr[16] = {0x00};
    unsigned int addr_1, addr_2, addr_3, addr_4, port_1, port_2;
    ftpObj->dataChannel = ftp_socket_create();
    if (ftp_send_and_recv_reply(ftpObj, "PASV") != FTP_RET_OK)
    {
        return false;
    }
    strcpy(pasv, strchr(ftpObj->replyString, '(') );
    sscanf(pasv, "(%d, %d, %d, %d, %d, %d)",
            &addr_1, &addr_2, &addr_3, &addr_4, &port_1, &port_2);
    sprintf(hostAddr, "%d.%d.%d.%d", addr_1, addr_2, addr_3, addr_4);
    if (ftp_socket_connect(ftpObj->dataChannel, hostAddr, (port_1 commandChannel, (struct sockaddr *)&dataAddr, &len) != 0)
    {
        return false;
    }
    ftpObj->dataChannel = ftp_socket_create();
    if (ftpObj->dataChannel == INVALID_SOCKET)
    {
        return false;
    }
    if (bind(ftpObj->dataChannel, (struct sockaddr *)&dataAddr, len) != 0)
    {
        return false;
    }
    if (getsockname(ftpObj->dataChannel, (struct sockaddr *)&dataAddr, &len) != 0)
    {
        return false;
    }
    if (ftp_socket_listen(ftpObj->dataChannel, 1) != 0)
    {
        return false;
    }
    addr = (char *)&dataAddr.sin_addr;
    port = (char *)&dataAddr.sin_port;
    sprintf(command, "PORT %d, %d, %d, %d, %d, %d",
            UC(addr[0]), UC(addr[1]), UC(addr[2]), UC(addr[3]),
            UC(port[0]), UC(port[1]) );
    if (ftp_send_and_recv_reply(ftpObj, command) != FTP_RET_OK)
    {
        return false;
    }
    return true;
}
static bool ftp_init_data_channel(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);
    if (ftpObj->dataChannel != INVALID_SOCKET)
    {
        ftp_socket_close(ftpObj->dataChannel);
    }
    if (ftpObj->transMode == FTP_MODE_PASV)
    {
        return ftp_pasv(ftpObj);
    }
    else
    {
        return ftp_port(ftpObj);
    }
    return false;
}
static bool ftp_build_data_channel(FTP_Obj *ftpObj)
{
    return_val_if_fail(ftpObj != NULL, false);
    if (ftpObj->transMode == FTP_MODE_PASV)
    {
        return true;
    }
    if (ftp_socket_select(ftpObj->dataChannel, FD_ACCEPT, ftpObj->secTimeOut) dataChannel);
    if (socketHandle == INVALID_SOCKET)
    {
        return false;
    }
    else
    {
        ftp_socket_close(ftpObj->dataChannel);
        ftpObj->dataChannel = socketHandle;
        ftp_set_nonblock(ftpObj->dataChannel);    // set nonblock
        return true;
    }
    return false;
}
#define MAX_RECV_DATA_SIZE 1024 * 100
static char* ftp_recv_data_channnel(FTP_Obj *ftpObj, const char *command)
{
    if (ftp_init_data_channel(ftpObj) != true)
    {
        ftp_socket_close(ftpObj->dataChannel);
        return NULL;
    }
    if (ftp_send_and_recv_reply(ftpObj, command) != FTP_RET_OK)
    {
        return NULL;
    }
    if (ftp_build_data_channel(ftpObj) != true)
    {
        return NULL;
    }
    char *recvBuf = (char *)malloc(sizeof(char) * MAX_RECV_DATA_SIZE);
    if (recvBuf == NULL)
    {
        return NULL;
    }
    memset(recvBuf, 0x00, MAX_RECV_DATA_SIZE);
    if (ftp_recv_command(ftpObj->dataChannel, recvBuf, MAX_RECV_DATA_SIZE) < 0)
    {
        return NULL;
    }
    return recvBuf;
}
static FTP_Ret ftp_list(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    char *recvBuf = ftp_recv_data_channnel(ftpObj, command);
    if (recvBuf != NULL)
    {
        printf("%s", recvBuf);
        SAFE_FREE(recvBuf);
        ftp_socket_close(ftpObj->dataChannel);
        if (ftp_get_reply(ftpObj) == FTP_RET_OK)
        {
            return FTP_RET_OK;
        }
    }
    return FTP_RET_FAIL;
}
static long ftp_get_file_size(const char *replyString)
{
    return_val_if_fail(replyString != NULL, -1);
    char sizeBuf[10] = {0x00};
    char *ptr = strchr(replyString, '(');
    int index;
    for (index = 0, ptr += 1; index < 10; ++index, ++ptr)
    {
        if (*ptr != ' ')
        {
            sizeBuf[index] = *ptr;
        }
        else
        {
            break;
        }
    }
    return atol(sizeBuf);
}
static bool ftp_get_file_name(const char *replyString, char *fileName)
{
    return_val_if_fail(replyString != NULL, -1);
    char *ptr = strstr(replyString, "for");
    if (ptr != NULL)
    {
        int index;
        // (ptr += 4) : ignore the "for " substring
        for (index = 0, ptr += 4; *ptr != '\0'; ++ptr, ++index)
        {
            if (*ptr != ' ')
            {
                fileName[index] = *ptr;
            }
            else
            {
                return true;
            }
        }
    }
    return false;
}
static FTP_Ret ftp_get_file(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    char *recvBuf = ftp_recv_data_channnel(ftpObj, command);
    if (recvBuf != NULL)
    {
        char fileName[256] = {0x00};
        long fileSize = ftp_get_file_size(ftpObj->replyString);
        if (ftp_get_file_name(ftpObj->replyString, fileName) == true)
        {
            int fd = open(fileName, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IRGRP);
            if (fileSize != 0)
            {
                long writeSize = write(fd, recvBuf, fileSize);
                if (writeSize != fileSize)
                {
                    return FTP_RET_FAIL;
                }
            }
            close(fd);
        }
        SAFE_FREE(recvBuf);
        ftp_socket_close(ftpObj->dataChannel);
        if (ftp_get_reply(ftpObj) == FTP_RET_OK)
        {
            return FTP_RET_OK;
        }
    }
    return FTP_RET_FAIL;
}
static bool ftp_put_file_name(const char *command, char *fileName)
{
    char *ptr = strchr(command, ' ');
    if (ptr != NULL)
    {
        int index = 0;
        for (ptr += 1; *ptr != '\0'; ++index, ++ptr)
        {
            fileName[index] = *ptr;
        }
        return true;
    }
    return false;
}
static long ftp_put_file_size(const char *fileName)
{
    struct stat buf;
    if (stat(fileName, &buf) == -1)
    {
        return -1;
    }
    return buf.st_size;
}
static FTP_Ret ftp_put_file(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    if (ftpObj->commandChannel == INVALID_SOCKET)
    {
        return FTP_RET_FAIL;
    }
    if (ftp_init_data_channel(ftpObj) != true)
    {
        ftp_socket_close(ftpObj->dataChannel);
        return FTP_RET_FAIL;
    }
    if (ftp_send_and_recv_reply(ftpObj, command) != FTP_RET_OK)
    {
        return FTP_RET_FAIL;
    }
    if (ftp_build_data_channel(ftpObj) != true)
    {
        return FTP_RET_FAIL;
    }
    char fileName[256] = {0x00};
    if (ftp_put_file_name(command, fileName) == true)
    {
        long size = ftp_put_file_size(fileName);
        printf("----->put: %s: %ld\n", fileName, size);
        int fd = open(fileName, O_RDONLY);
        if (fd != -1)
        {
            char *recvBuf = (char *)malloc(sizeof(char) * MAX_RECV_DATA_SIZE);
            if (recvBuf != NULL)
            {
                ssize_t readSize = read(fd, recvBuf, size);
                if (readSize dataChannel, recvBuf, readSize) != -1)
                {
                    close(fd);
                    ftp_socket_close(ftpObj->dataChannel);
                }
                return ftp_get_reply(ftpObj);
            }
        }
    }
    return FTP_RET_FAIL;
}
static FTP_Ret ftp_delete_file(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}
static FTP_Ret ftp_make_directory(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}
static FTP_Ret ftp_remove_directory(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}
static FTP_Ret ftp_change_directory(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, command);
}
static FTP_Ret ftp_pwd(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    return ftp_send_and_recv_reply(ftpObj, "PWD");
}
static FTP_Ret ftp_close(FTP_Obj *ftpObj, const char *command)
{
    return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
    return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
    if (ftpObj->commandChannel != INVALID_SOCKET)
    {
        FTP_Ret ret = ftp_send_and_recv_reply(ftpObj, command);
        if (ret == FTP_RET_OK)
        {
            ftp_socket_close(ftpObj->commandChannel);
        }
        else
        {
            return FTP_RET_FAIL;
        }
    }
    if (ftpObj->dataChannel != INVALID_SOCKET)
    {
        ftp_socket_close(ftpObj->dataChannel);
    }
    SAFE_FREE(ftpObj);
    return FTP_RET_OK;
}
static bool ftp_command_filter(const char *command, char *filter)
{
    int index = 0;
    for ( ; *command != '\0'; ++index, ++command)
    {
        if (*command != ' ')
        {
            filter[index] = *command;
        }
        else
        {
            return true;
        }
    }
    return false;
}
static bool ftp_replay_command(char *sendCommand, const char *commandName)
{
    char temp[COMMAND_SIZE] = {0x00};
    char *ptr = strchr(sendCommand, ' ');
    if (ptr == NULL)
    {
        strcpy(sendCommand, commandName);
        return true;
    }
    strcpy(temp, commandName);
    strcat(temp, ptr);
    if (strlen(temp) = 0)
            {
                ftp_replay_command(enterCommand, replace_command[index]);
                printf("-------->%s\n", enterCommand);
                ftp_command_func[index](ftpObj, enterCommand);
            }
            if (strcmp(enterCommand, "QUIT") == 0)
            {
                break;
            }
            else if (strcmp(enterCommand, "") == 0)
            {
                continue;
            }
        }
    }
    return FTP_RET_OK;
}

  
main.c:
  


main.c



#include "ftp.h"
#include "unp.h"
#define DEBUG    0
#define PORT    21
static void ftp_test(const char *ipAddress)
{
    FTP_entry(ipAddress, PORT);
    return;
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: ./ftp_client \n");
        return -1;
    }
    ftp_test(argv[1]);
    return 0;
}

  
最后给了Makefile:
  


Makefile



CC=gcc
CFILES=main.c ftp.c ftp_socket.c
OBJ+=$(CFILES:.c=.o)
EXE=ftp_client
all:   $(EXE)
$(EXE)    :    $(OBJ) $(LIBFILES)
    $(CC) -Wall $(CFILES:.c=.o) -o $@ -lunp -lcurses
clean:
    rm -f  *.o *.gdb $(EXE)
.SUFFIXES: .cpp  .c .o
.c.o:
    $(CC) -Wall -o $*.o -c $(CCFLAGS) $*.c

  
  END

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-71052-1-1.html 上篇帖子: FTP 和HTTP 下篇帖子: ftp中ftpClient类的API--
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表