Chinaunix首页 | 论坛 | 博客
  • 博客访问: 30103227
  • 博文数量: 230
  • 博客积分: 2868
  • 博客等级: 少校
  • 技术积分: 2223
  • 用 户 组: 普通用户
  • 注册时间: 2009-10-08 21:48
个人简介

Live & Learn

文章分类

全部博文(230)

文章存档

2022年(2)

2019年(5)

2018年(15)

2017年(42)

2016年(24)

2015年(13)

2014年(1)

2012年(5)

2011年(58)

2010年(56)

2009年(9)

我的朋友

分类: LINUX

2016-08-06 10:31:59

linux下面编程,发现写一些API小程序,C预言的编程大部分是在调用了与文件有关的IO操作,需要终结一下最好的办法就是做一个小小的应用编程,基于文件描述符的io的操作,和基于流的IO操作。下面主要基于 LINUX 平台,对 FTP 客户端的实现原理进行详尽的解释并阐述如何使用 C 语言编写一个简单的 FTP 客户端。(注意ftp的客服端最好是linux的服务器,如:连接server—u的服务就会不不成功,因为编码不同,要想连接成功需要解决相关的编码问题。

ftp 概述:
文件传输协议(FTP)作为网络共享文件的传输协议,在网络应用软件中具有广泛的应用。FTP的目标是提高文件的共享性和可靠高效地传送数据。
在传输文件时,FTP 客户端程序先与服务器建立连接,然后向服务器发送命令。服务器收到命令后给予响应,并执行命令。FTP 协议与操作系统无关,任何操作系统上的程序只要符合 FTP 协议,就可以相互传输数据。
ftp的协议:
 FTP 是 TCP/IP 协议组中的协议之一,FTP 使用 2 个端口,一个数据端口20和一个命令端口21(也叫做控制端口),具体可以上网找资料。
ftp的工作模式:
ftp的工作模式可以分为主动模式(PORT)和被动模式(PASV)
主动模式 (PORT) 
主动模式下,客户端随机打开一个大于 1024 的端口向服务器的命令端口 P,即 21 端口,发起连接,同时开放N +1 端口监听,并向服务器发出 “port N+1” 命令,由服务器从它自己的数据端口 (20) 主动连接到客户端指定的数据端口 (N+1)。 FTP 的客户端只是告诉服务器自己的端口号,让服务器来连接客户端指定的端口。对于客户端的防火墙来说,这是从外部到内部的连接,可能会被阻塞。
 被动模式 (PASV)
  为了解决服务器发起到客户的连接问题,有了另一种 FTP 连接方式,即被动方式。命令连接和数据连接都由客户端发起,这样就解决了从服务器到客户端的数据端口的连接被防火墙过滤的问题。被动模式下,当开启一个 FTP 连接时,客户端打开两个任意的本地端口 (N > 1024 和 N+1) 。第一个端口连接服务器的 21 端口,提交 PASV 命令。然后,服务器会开启一个任意的端口 (P > 1024 ),返回如“227 entering passive mode (127,0,0,1,22,33)”。 它返回了 227 开头的信息,在括号中有以逗号隔开的六个数字,前四个指服务器的地址,最后两个,将倒数第二个乘 256 再加上最后一个数字,这就是 FTP 服务器开放的用来进行数据传输的端口。如得到 227 entering passive mode (h1,h2,h3,h4,p1,p2),那么端口号是 p1*256+p2,ip 地址为h1.h2.h3.h4。这意味着在服务器上有一个端口被开放。客户端收到命令取得端口号之后, 会通过 N+1 号端口连接服务器的端口 P,然后在两个端口之间进行数据传输。
命令端口:
一般来说,客户端有一个 Socket 用来连接 FTP 服务器的相关端口(21就是命令接收端口),它负责 FTP 命令的发送和接收返回的响应信息。一些操作如“登录”、“改变目录”、“删除文件”,依靠这个连接发送命令就可完成。
数据端口:
对于有数据传输的操作,主要是显示目录列表,上传、下载文件,我们需要依靠另一个 Socket来完成。
如果使用被动模式,通常服务器端会返回一个端口号。客户端需要用另开一个 Socket 来连接这个端口,然后我们可根据操作来发送命令,数据会通过新开的一个端口传输。
如果使用主动模式,通常客户端会发送一个端口号给服务器端,并在这个端口监听。服务器需要连接到客户端开启的这个数据端口,并进行数据的传输。
FTP 每个命令都有 3 到 4 个字母组成,命令后面跟参数,用空格分开。每个命令都以 "/r/n"结束。 要下载或上传一个文件,首先要登入  FTP 服务器,然后发送命令,最后退出。
这个过程中,主要用到的命令有 USER、PASS、SIZE、REST、CWD、RETR、PASV、QUIT、LIST、....。 
USER: 指定用户名。通常是控制连接后第一个发出的命令。“USER fengbo/r/n”: 用户名为fengbo 登录。 
PASS: 指定用户密码。该命令紧跟 USER 命令后。“PASS fengbo/r/n”:密码为 fengbo。 
SIZE: 从服务器上返回指定文件的大小。“SIZE file.txt/r/n”:如果 file.txt 文件存在,则返回该文件的大小。
  CWD: 改变工作目录。如:“CWD dir_name/r/n”。 LIST: 相当于dir或者是ls命令“LIST dir_name/r/n”。 
PASV: 让服务器在数据端口监听,进入被动模式。如:“PASV/r/n”。 
PORT: 告诉 FTP 服务器客户端监听的端口号,让 FTP 服务器采用主动模式连接客户端。如:“PORT h1,h2,h3,h4,p1,p2”。 
RETR: 下载文件。“RETR down_file.txt /r/n”:下载文件 down_file.txt。 
STOR: 上传文件。“STOR up_file.txt/r/n”:上传文件 up_file.txt。
  REST: 该命令并不传送文件,而是略过指定点后的数据。此命令后应该跟其它要求文件传输的 FTP 命令。“REST 100/r/n”:重新指 定文件传送的偏移量为 100 字节。
  QUIT: 关闭与服务器的连接。
ftp的编程基础:
ftp的编程是基于套接字(socket)的编程。如图:
linux的ftp客服端编程(1) - xfb2020 - fengbo的博客

 ftp具体的工作连接示意图:
linux的ftp客服端编程(1) - xfb2020 - fengbo的博客
 

、=====================================
上一个项目写了个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 : 声明和定义了一些类型和操作

复制代码
代码
		
		
#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底层的处理

复制代码
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

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



======================================================================
/server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define N 256

typedef struct sockaddr SA;

void commd_ls(int);
void commd_get(int, char *);
void commd_put(int, char *);

int main(int arg, char *argv[])
{
    int ser_sockfd,cli_sockfd;
    struct sockaddr_in ser_addr,cli_addr;
    int ser_len, cli_len;
    char commd [N];
    bzero(commd,N);

    if((ser_sockfd=socket(AF_INET, SOCK_STREAM, 0) ) < 0)
    {
        printf("Sokcet Error!\n");
        return -1;
    }

    bzero(&ser_addr,sizeof(ser_addr));
    ser_addr.sin_family = AF_INET;
    ser_addr.sin_addr.s_addr = htonl(INADDR_ANY); 
    ser_addr.sin_port = htons ( 8989 );
    ser_len = sizeof(ser_addr);

    if((bind(ser_sockfd, (SA *)&ser_addr, ser_len)) < 0)
    {
        printf("Bind Error!\n");
        return -1;
    }

    if(listen(ser_sockfd, 5) < 0)
    {
        printf("Linsten Error!\n");
        return -1;
    }

    bzero(&cli_addr, sizeof(cli_addr));
    ser_len = sizeof(cli_addr);

    while(1)
    {
        printf("server_ftp>");

        if((cli_sockfd=accept(ser_sockfd, (SA *)&cli_addr, &cli_len)) < 0)
        {
            printf("Accept Error!\n");
            exit(1);
        }

        if(read(cli_sockfd, commd, N) < 0)
        {
            printf("Read Error!\n");
            exit(1);
        }

        printf("recvd [ %s ]\n",commd);

        if(strncmp(commd,"ls",2) == 0)
        {
            commd_ls(cli_sockfd);
        }else if(strncmp(commd,"get", 3) == 0 )
        {
            commd_get(cli_sockfd, commd+4);
        }else if(strncmp(commd, "put", 3) == 0)            
        {
            commd_put(cli_sockfd, commd+4);
        }else
        {
            printf("Error!Command Error!\n");
        }
    }

    return 0;
}

void commd_ls(int sockfd)
{
    DIR * mydir =NULL;
    struct dirent *myitem = NULL;
    char commd[N] ;
    bzero(commd, N);

    if((mydir=opendir(".")) == NULL)
    {
        printf("OpenDir Error!\n");
        exit(1);
    }

    while((myitem = readdir(mydir)) != NULL)
    {
        if(sprintf(commd, myitem->d_name, N) < 0)
        {
            printf("Sprintf Error!\n");
            exit(1);
        }

        if(write(sockfd, commd, N) < 0 )
        {
            printf("Write Error!\n");
            exit(1);
        }
    }

    closedir(mydir);
    close(sockfd);

    return ;
}

void commd_get(int sockfd, char *filename)
{
    int fd, nbytes;
    char buffer[N];
    bzero(buffer, N);    
    
    printf("get filename : [ %s ]\n",filename);
    if((fd=open(filename, O_RDONLY)) < 0)
    {
        printf("Open file Error!\n");
        buffer[0]='N';
        if(write(sockfd, buffer, N) <0)
        {
            printf("Write Error!At commd_get 1\n");
            exit(1);
        }
        return ;
    }

    buffer[0] = 'Y';
    if(write(sockfd, buffer, N) <0)
    {
        printf("Write Error! At commd_get 2!\n");
        close(fd);
        exit(1);
    }

    while((nbytes=read(fd, buffer, N)) > 0)
    {
        if(write(sockfd, buffer, nbytes) < 0)
        {
            printf("Write Error! At commd_get 3!\n");
            close(fd);
            exit(1);
        }
    }

    close(fd);
    close(sockfd);

    return ;
}

void commd_put(int sockfd, char *filename)
{
    int fd, nbytes;
    char buffer[N];
    bzero(buffer, N);    
    
    printf("get filename : [ %s ]\n",filename);
    if((fd=open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
    {
        printf("Open file Error!\n");
        return ;
    }

    while((nbytes=read(sockfd, buffer, N)) > 0)
    {
        if(write(fd, buffer, nbytes) < 0)
        {
            printf("Write Error! At commd_put 1!\n");
            close(fd);
            exit(1);
        }
    }

    close(fd);
    close(sockfd);

    return ;
}

//client.c
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define N 256

typedef struct sockaddr SA;

void commd_help();
void commd_exit();
void commd_ls(struct sockaddr_in, char *);
void commd_get(struct sockaddr_in , char *);
void commd_put(struct sockaddr_in , char *);

int main(int argc, char *argv[])
{
    char commd[N];
    struct sockaddr_in addr;
    int len;
    bzero(&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8989);
    len = sizeof(addr);

    while(1)
    {
        printf("ftp>");
        bzero(commd,N);
        if(fgets(commd,N,stdin) == NULL)
        {
            printf("Fgets Error!\n");
            return -1;
        }

        commd[strlen(commd)-1]='\0';

        printf("Input Command Is [ %s ]\n",commd);

        if(strncmp(commd,"help",4) == 0)
        {
            commd_help();
        }else if(strncmp(commd, "exit",4) == 0)
        {
            commd_exit();
            exit(0);
        }else if(strncmp(commd, "ls" , 2) == 0)
        {
            commd_ls(addr, commd);
        }else if(strncmp(commd, "get" , 3) == 0)
        {
            commd_get(addr, commd);
        }else if(strncmp(commd, "put", 3) ==0 )
        {
            commd_put(addr, commd);
        }else
        {
            printf("Command Is Error!Please Try Again!\n");
        }

    }    
    return 0;
}

void commd_help()
{
    
    printf("\n=------------------- Welcome to Use the Ftp ----------------=\n");
    printf("|                                                           |\n");
    printf("|  help : Display All Command for the Server                |\n");
    printf("|                                                           |\n");
    printf("|   exit: Quit The Sever                                    |\n");
    printf("|                                                           |\n");
    printf("|   ls : Display All file On the Ftp Server                 |\n");
    printf("|                                                           |\n");
    printf("| get : Download FIle from the Ftp Server             |\n");
    printf("|                                                           |\n");
    printf("| put : Upload FIle to the Ftp Server                 |\n");
    printf("|                                                           |\n");
    printf("=-----------------------------------------------------------=\n");

    return ;
}

void commd_exit()
{
    printf("Byte!\n");
}

void commd_ls(struct sockaddr_in addr, char *commd)
{
    int sockfd;

    if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Socket Error!\n");
        exit(1);
    }    

    if(connect(sockfd, (SA *)&addr, sizeof(addr)) < 0)
    {
        printf("Connect Error!\n");
        exit(1);
    }
    
    if(write(sockfd, commd, N) < 0)
    {
        printf("Write Error!\n");
        exit(1);
    }

    while(read(sockfd, commd, N) > 0)
    {
        printf(" %s ",commd);
    }
    printf("\n");

    close(sockfd);
    return ;
}

void commd_get(struct sockaddr_in addr, char *commd)
{
    int fd;
    int sockfd;
    char buffer[N];
    int nbytes;

    if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Socket Error!\n");
        exit(1);
    }

    if(connect(sockfd, (SA *)&addr, sizeof(addr)) < 0)
    {
        printf("Connect Error!\n");
        exit(1);
    }
    
    if(write(sockfd, commd, N) < 0)
    {
        printf("Write Error!At commd_get 1\n");
        exit(1);
    }

    if(read(sockfd, buffer, N) < 0)
    {
        printf("Read Error!At commd_get 1\n");
        exit(1);
    }

    if(buffer[0] =='N')
    {
        close(sockfd);
        printf("Can't Open The File!\n");
        return ;
    }

    if((fd=open(commd+4, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
    {
        printf("Open Error!\n");
        exit(1);
    }

    while((nbytes=read(sockfd, buffer, N)) > 0)
    {
        if(write(fd, buffer, nbytes) < 0)
        {
            printf("Write Error!At commd_get 2");
        }
    }

    close(fd);
    close(sockfd);

    return ;

}

void commd_put(struct sockaddr_in addr, char *commd)
{
    int fd;
    int sockfd;
    char buffer[N];
    int nbytes;

    if((sockfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("Socket Error!\n");
        exit(1);
    }

    if(connect(sockfd, (SA *)&addr, sizeof(addr)) < 0)
    {
        printf("Connect Error!\n");
        exit(1);
    }
    
    if(write(sockfd, commd, N)<0)
    {
        printf("Wrtie Error!At commd_put 1\n");
        exit(1);
    }

    if((fd=open(commd+4, O_RDONLY)) < 0)
    {
        printf("Open Error!\n");
        exit(1);
    }

    while((nbytes=read(fd, buffer, N)) > 0)
    {
        if(write(sockfd, buffer, nbytes) < 0)
        {
            printf("Write Error!At commd_put 2");
        }
    }

    close(fd);
    close(sockfd);

    return ;
}

不好意思忘写注释了,在这里跟大家分享一下个人收获。在一开始写这个程序时,只要用到自己定义的命令服务端就挂掉了,这个问题困扰许久,后来才发现自己写的代码中对于出错处理时用到的函数是exit(1),而不是用的return。后来把exit(1)改写成return 问题就解决了,后来查资料得出exit()是退出当前的进程,而return 只是退出函数体而并不结束进程。还遇到了另外的一个问题,就是我自己定义的ftp中的命令只能运行一次,第二次运行时就会报connect失败,当服务端先于客户端退出时再次运行服务端时也会出现这样的问题,当过一段时间再次运行时就会解决这个问题,可能是系统回收时有点慢的原因,但是自己的定义命令还是出现同样的问题,后来把套接字的创建和链接都从客户端的主程序中移到了函数中去做了,问题就解决了,这样看来跟服务端先于客户端退出时出现的问题应该是一样的,但是我们的程序不可能停下来等,所以,只能是修改自己的程序来解决这个问题,于是就让每个函数来自己创建套接字使用完后再关闭它,这样问题就解决了。到这我的收获就说完了,如果谁有这方面的体会心得希望能一起分享给大家!!
========================================================================================================

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;
}


阅读(1211) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~