Live & Learn
分类: LINUX
2016-08-06 10:31:59
linux下面编程,发现写一些API小程序,C预言的编程大部分是在调用了与文件有关的IO操作,需要终结一下最好的办法就是做一个小小的应用编程,基于文件描述符的io的操作,和基于流的IO操作。下面主要基于 LINUX 平台,对 FTP 客户端的实现原理进行详尽的解释并阐述如何使用 C 语言编写一个简单的 FTP 客户端。(注意ftp的客服端最好是linux的服务器,如:连接server—u的服务就会不不成功,因为编码不同,要想连接成功需要解决相关的编码问题。)
这一次我自己在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 : 声明和定义了一些类型和操作
#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 << 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底层的处理
#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里面
#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的操作实现,这部分相对比较长,直接折叠起来:
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:
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
ftp.h文件
#ifndef __FTP_H_
#define __FTP_H_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "common.h"
int ftpcmd(int sockftp,char *fmt,...);
int ftp(char
*host,char *port,char *user,char *pass,char *filename,char
*savefile);
#endif
ftp.c文件
#include "ftp.h"
int verbose = 1;
FILE *ftpio = NULL;
char buf[4096];
int main(int argc,char *argv[])
{
if (argc<6)
{
dprint("usgs:%s
\n",argv[0]);
exit(-1);
}
ftp(argv[1],argv[2],argv[3],argv[4],argv[5],argv[6]);
return 1;
}
int ftp(char *host,char *port,char *user,char *pass,char *filename,char
*savefile)
{
int sockftp = -1,sockdata = -1,sockxfer = -1;
struct sockaddr_in addr;
unsigned long hostip;
int
len,tmp,err;
int retval = -1;
int savefd;
unsigned char *c,*p;
struct hostent *he;
hostip =
inet_addr(host);
if (hostip == -1)
{
he =
gethostbyname(host);
if (he == NULL)
return -1;
hostip = * (unsigned long
*)he->h_addr;
}
sockftp =
socket(AF_INET,SOCK_STREAM,0);
if (sockftp == -1)
goto out;
addr.sin_family = PF_INET;
//addr.sin_port =
htons(21);
addr.sin_port = htons(atoi(port));
addr.sin_addr.s_addr = hostip;
if (connect(sockftp,(struct
sockaddr *)&addr,sizeof(addr)) == -1)
goto
out;
err = ftpcmd(sockftp,NULL);
if (err !=
220)
goto out;
err = ftpcmd(sockftp,"USER
%s",user);
if (err != 331)
goto out;
err = ftpcmd(sockftp,"PASS %s",pass);
if (err !=
230)
goto out;
err = ftpcmd(sockftp,"TYPE
I");
if (err != 200)
goto out;
sockdata = socket(AF_INET,SOCK_STREAM,0);
if (sockdata ==
-1)
goto out;
tmp = sizeof(addr);
getsockname(sockftp,(struct sockaddr *)&addr,&tmp);
addr.sin_port = 0;
if (bind(sockdata,(struct sockaddr
*)&addr,sizeof(addr)) == -1)
goto out;
if
(listen(sockdata,1) == -1)
goto out;
tmp =
sizeof(addr);
getsockname(sockdata,(struct sockaddr
*)&addr,&tmp);
c = (unsigned char
*)&addr.sin_addr;
p = (unsigned char
*)&addr.sin_port;
err = ftpcmd(sockftp,"PORT
%d,%d,%d,%d,%d,%d",
c[0],c[1],c[2],c[3],p[0],p[1]);
if (err != 200)
goto out;
err =
ftpcmd(sockftp,"RETR %s",filename);
if (err != 150)
goto out;
tmp = sizeof(addr);
sockxfer =
accept(sockdata,(struct sockaddr *)&addr,&tmp);
if (sockxfer
== -1)
goto out;
savefd =
open(savefile,O_WRONLY|O_CREAT,0644);
if (savefd ==
-1)
goto out;
retval = 0;
while
((len=read(sockxfer,buf,sizeof(buf)))>0)
{
write(savefd,buf,len);
retval += len;
};
close(savefd);
out:
close(sockxfer);
close(sockdata);
close(sockftp);
if (ftpio)
{
fclose(ftpio);
ftpio = NULL;
}
return retval;
}
int ftpcmd(int sockftp,char *fmt,...)
{
va_list vp;
int err,len;
if (ftpio == NULL)
{
ftpio = fdopen(sockftp,"r");
if (ftpio ==
NULL)
return -1;
}
if
(fmt)
{
va_start(vp,fmt);
len =
vsprintf(buf,fmt,vp);
buf[len++] = '\r';
buf[len++]='\n';
write(sockftp,buf,len);
if (verbose)
write(1,buf,len);
}
dprint("command is %s\n",fmt);
do
{
if
(fgets(buf,sizeof(buf),ftpio) == NULL)
return
-1;
if (verbose)
dprint(buf);
} while(buf[3] == '-');
sscanf(buf,"%d",&err);
return err;
}