这一次我自己在Linux下写了一个简单的FTP客户端,支持的操作只有那些最常用的。这个程序已经通过简单的一些测试,目前实现的操作有: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 : 声明和定义了一些类型和操作
#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.h
#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 << FD_READ_BIT)
#define FD_WRITE_BIT 1
#define FD_WRITE (1 << FD_WRITE_BIT)
#define FD_OOB_BIT 2
#define FD_OOB (1 << FD_OOB_BIT)
#define FD_ACCEPT_BIT 3
#define FD_ACCEPT (1 << FD_ACCEPT_BIT)
#define FD_CONNECT_BIT 4
#define FD_CONNECT (1 << FD_CONNECT_BIT)
#define FD_CLOSE_BIT 5
#define FD_CLOSE (1 << FD_CLOSE_BIT)
typedef int SOCKET_HANDLE;
typedef struct _FTP_Info
{
char servIP[20];
int servPort;
char userName[20];
char userPassword[20];
} FTP_Info;
int ftp_socket_connect(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port);
SOCKET_HANDLE ftp_socket_create(void);
int ftp_socket_select(SOCKET_HANDLE socketHandle, int event, int secTime);
FTP_Ret ftp_socket_close(SOCKET_HANDLE socketHandle);
FTP_Ret ftp_socket_listen(SOCKET_HANDLE socketHandle, int maxListen);
int ftp_socket_accept(SOCKET_HANDLE socketHandle);
FTP_Ret
ftp_socket_bind_and_listen(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port);
DECLS_END
#endif
ftp_socket.c: socket底层的处理
ftp_socket.c
#include "unp.h"
#include "ftp_socket.h"
int ftp_socket_connect(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, -1);
return_val_if_fail(ipAddress != NULL, -1);
struct sockaddr_in servAddr;
bzero(&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(port);
Inet_pton(AF_INET, ipAddress, &servAddr.sin_addr);
return connect(socketHandle, (SA *)&servAddr, sizeof(servAddr));
}
FTP_Ret
ftp_socket_bind_and_listen(SOCKET_HANDLE socketHandle, const char *ipAddress, const int port)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
return_val_if_fail(ipAddress != NULL, FTP_RET_INVALID_PARAMS);
struct sockaddr_in servAddr;
bzero(&servAddr, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(port);
Bind(socketHandle, (SA *)&servAddr, sizeof(servAddr));
Listen(socketHandle, LISTENQ);
return FTP_RET_OK;
}
FTP_Ret ftp_socket_listen(SOCKET_HANDLE socketHandle, int maxListen)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
Listen(socketHandle, maxListen);
return FTP_RET_OK;
}
int ftp_socket_accept(SOCKET_HANDLE socketHandle)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
return accept(socketHandle, NULL, NULL);
}
SOCKET_HANDLE ftp_socket_create(void)
{
return Socket(AF_INET, SOCK_STREAM, 0);
}
int ftp_socket_select(SOCKET_HANDLE socketHandle, int event, int secTime)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
struct timeval timeValue;
fd_set readSet, writeSet;
FD_ZERO(&readSet);
if ( (event & FD_ACCEPT) or (event & FD_READ) or (event & FD_CLOSE) )
{
FD_SET(socketHandle, &readSet);
}
FD_ZERO(&writeSet);
if ( (event & FD_CONNECT) or (event & FD_WRITE) )
{
FD_SET(socketHandle, &writeSet);
}
timeValue.tv_sec = secTime;
timeValue.tv_usec = secTime * 1000;
return select(socketHandle + 1, &readSet, &writeSet, NULL, &timeValue);
}
FTP_Ret ftp_socket_close(SOCKET_HANDLE socketHandle)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
close(socketHandle);
socketHandle = INVALID_SOCKET;
return FTP_RET_OK;
}
ftp.h: 只给出一个FTP进入点的接口,其他FTP操作都封装在ftp.c里面
ftp.h
#include "ftp_socket.h"
#ifndef FTP_H
#define FTP_H
DECLS_BEGIN
#define FTP_REPLY_SIZE 512
typedef enum _mode
{
FTP_MODE_PASV,
FTP_MODE_PORT
} FTP_trans_mode;
typedef struct _FTP_obj
{
SOCKET_HANDLE commandChannel;
SOCKET_HANDLE dataChannel;
unsigned int secTimeOut;
int replyCode;
char replyString[FTP_REPLY_SIZE];
FTP_trans_mode transMode;
} FTP_Obj;
typedef FTP_Ret (*FtpCommandFunc)(FTP_Obj *ftpObj, const char *command);
FTP_Ret FTP_entry(const char *ipAddress, const int port);
DECLS_END
#endif
ftp.c : FTP核心代码,包含FTP的操作实现,这部分相对比较长:
ftp.c
#include <curses.h>
#include "ftp.h"
#include "unp.h"
#define FTP_PROMPT "ftp> "
#define COMMAND_SIZE 256
#define MAX_PROMPT_SIZE 128
#define MAX_RECV_SIZE 1024 << 1
static bool ftp_set_nonblock(SOCKET_HANDLE socketHandle)
{
int flag = fcntl(socketHandle, F_GETFL);
flag |= O_NONBLOCK;
if (fcntl(socketHandle, F_SETFL, flag) == -1)
{
return false;
}
return true;
}
static int ftp_send_buffer(SOCKET_HANDLE socketHandle, const char *buffer, size_t size)
{
return write(socketHandle, buffer, size);
}
static FTP_Ret ftp_send_command(SOCKET_HANDLE socketHandle, const char *command)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, FTP_RET_INVALID_PARAMS);
return_val_if_fail(command != NULL, FTP_RET_INVALID_PARAMS);
char sendCommand[COMMAND_SIZE] = {0x00};
sprintf(sendCommand, "%s\r\n", command);
Writen(socketHandle, sendCommand, strlen(sendCommand));
return FTP_RET_OK;
}
static long
ftp_recv_command(SOCKET_HANDLE socketHandle, char *recvBuf, unsigned long recvMaxSize)
{
return_val_if_fail(socketHandle != INVALID_SOCKET, -1);
return_val_if_fail(recvBuf != NULL, -1);
long recvSize = 0;
long totalSize = 0;
do
{
recvSize = recv(socketHandle, recvBuf + totalSize, recvMaxSize - totalSize, 0);
totalSize += recvSize;
} while (recvSize > 0);
return totalSize;
}
static FTP_Ret ftp_init_obj(FTP_Obj *ftpObj)
{
return_val_if_fail(ftpObj != NULL, FTP_RET_INVALID_PARAMS);
ftpObj->commandChannel = INVALID_SOCKET;
ftpObj->dataChannel = INVALID_SOCKET;
ftpObj->secTimeOut = 60;
ftpObj->replyCode = -1;
ftpObj->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) <= 0)
{
fprintf(stderr, "Recv reply message failed.\n");
return FTP_RET_FAIL;
}
else
{
printf("%s", recvBuf);
strncpy(ftpObj->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 << 8) | port_2) != 0)
{
return false;
}
return true;
}
#define UC(arg) ( ( (int)arg) & 0xff)
static bool ftp_port(FTP_Obj *ftpObj)
{
return_val_if_fail(ftpObj != NULL, false);
char *addr, *port;
char command[256] = {0x00};
struct sockaddr_in dataAddr;
size_t len = sizeof(dataAddr);
if (getsockname(ftpObj->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) <= 0)
{
return false;
}
SOCKET_HANDLE socketHandle = ftp_socket_accept(ftpObj->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 <= 0)
{
return FTP_RET_FAIL;
}
if (ftp_send_buffer(ftpObj->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) <= COMMAND_SIZE)
{
strcpy(sendCommand, temp);
return true;
}
return false;
}
typedef struct _ftp_do_command
{
char command[COMMAND_SIZE];
FtpCommandFunc ftp_do_command;
} ftp_command_tag;
const FtpCommandFunc ftp_command_func[] =
{
ftp_list,
ftp_list,
ftp_pwd,
ftp_change_directory,
ftp_set_transfer_type,
ftp_set_transfer_type,
ftp_set_transfer_mode,
ftp_get_file,
ftp_put_file,
ftp_delete_file,
ftp_syst,
ftp_make_directory,
ftp_remove_directory,
ftp_close,
ftp_close,
};
const char *command_name[] =
{
"ls", "dir", "pwd", "cd", "ascii", "binary",
"passive", "get", "put", "delete", "system",
"mkdir", "rmdir", "quit", "bye",
};
const char *replace_command[] =
{
"LIST", "LIST", "PWD", "CWD", "TYPE A", "TYPE I",
"passive", "RETR", "STOR", "DELE", "SYST",
"MKD", "RMD", "QUIT", "QUIT",
};
static int ftp_return_command_index(const char *command)
{
int index;
int number = sizeof(command_name) / sizeof(char *);
for (index = 0; index < number; ++index)
{
if (strcmp(command, command_name[index]) == 0)
{
return index;
}
}
return -1;
}
FTP_Ret FTP_entry(const char *ipAddress, const int port)
{
FTP_Obj *ftpObj = ftp_connect_server(ipAddress, port);
if (ftpObj != NULL)
{
ftp_login(ftpObj, ipAddress);
ftp_syst(ftpObj, NULL);
ftp_set_transfer_mode(ftpObj, "TYPE I");
while (1)
{
char enterCommand[256] = {0x00};
Writen(1, FTP_PROMPT, sizeof(FTP_PROMPT));
memset(enterCommand, 0x00, sizeof(enterCommand));
Fgets(enterCommand, 256, stdin);
enterCommand[strlen(enterCommand) - 1] = '\0';
char filter[256] = {0x00};
ftp_command_filter(enterCommand, filter);
int index = ftp_return_command_index(filter);
if (index >= 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 <IP>\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
原文地址:http://www.cnblogs.com/python_newbie/archive/2010/08/01/1789954.html