关于FTP(未完)

一、FTP两种工作模式:
    主动模式(Active FTP)和被动模式(Passive FTP)
    
    在主动模式下,FTP客户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,然后开放N+1号端口进行
监听,并向服务器发出PORT N+1命令。服务器接收到命令后,会用其本地的FTP数据端口(通常是20)来连接客户端指
定的端口N+1,进行数据传输。 

    在被动模式下,FTP库户端随机开启一个大于1024的端口N向服务器的21号端口发起连接,同时会开启N+1号端口。然后向服务器发送PASV命令,通 知服务器自己处于被动模式。服务器收到命令后,会开放一个大于1024的端口P进行监听,然后用PORT P命令通知客户端,自己的数据端口是P。客户端收到命令后,会通过N+1号端口连接服务器的端口P,然后在两个端口之间进行数据传输。 

    被动模式的FTP通常用在处于防火墙之后的FTP客户访问外界FTp服务器的情况,因为在这种情况下,防火墙通常配置为不允许外界访问防火墙之后主机,而 只允许由防火墙之后的主机发起的连接请求通过。因此,在这种情况下不能使用主动模式的FTP传输,而被动模式的FTP可以良好的工作。

    总的来说,主动模式的FTP是指服务器主动连接客户端的数据端口,被动模式的FTP是指服务器被动地等待客户端连接自己的数据端口。
 
二、最近遇到个烦人的事情是移植的ftp客户端,利用脚本调用的,如果调用的频率很高的情况下,会发现CPU老高了,根本达不到实际的应用要求,机器跑起来也很累,干脆就从经典的ftp客户端源码中挖了一部分特别简单的句子出来了。原来是移植的netkit-ftp-0.17.tar.gz整个包,其实busybox里面应该是有的,当时使用的是被动模式,简单的用脚本限定从根目录到二级目录可传:
#!/bin/sh
############################################################
# examples:
# Tow level dir: ./ftpput.sh "192.168.1.20" "21" "test" "test" "/mnt/config/factory.conf" "/mnt/nfs/factory.conf"
# One level dir: ./ftpput.sh "192.168.1.20" "21" "test" "test" "/mnt/config/factory.conf" "/upload/factory.conf"
# Root dir: ./ftpput.sh "192.168.1.20" "21" "test" "test" "/mnt/config/factory.conf" "factory.conf"
# August 10, 2014, 15:33
##########################################################
if [ $# -ne 6  ]
then  
    echo "Usage $0 <ip> <port> <user> <password> <local_dir/filename> <remote_dir/filename>"
    exit 1
fi

LOCALDIR=`dirname $5`
LOCALFILE=`basename $5`
REMOTEDIR=`dirname $6`
REMOTEFILE=`basename $6`
FTP_PATH=/mnt/bin/ftp

#only root dir, set remotedir to NULL
if [ "$REMOTEDIR" = "." ]; then
	REMOTEDIR=""
fi

TOP_REMOTE_DIR=`echo $REMOTEDIR | cut -d "/" -f 2`
SECOND_REMOTE_DIR=`echo $REMOTEDIR | cut -d "/" -f 3`
THIRD_REMOTE_DIR=`echo $REMOTEDIR | cut -d "/" -f 4`

#show info
#echo "LOCALDIR=$LOCALDIR"
#echo "LOCALFILE=$LOCALFILE"
#echo "REMOTEDIR=$REMOTEDIR"
#echo ""
#echo "TOP_REMOTE_DIR=$TOP_REMOTE_DIR"
#echo "SECOND_REMOTE_DIR=$SECOND_REMOTE_DIR"
#echo "THIRD_REMOTE_DIR=$THIRD_REMOTE_DIR"
#echo "REMOTEFILE=$REMOTEFILE"

if [ "$THIRD_REMOTE_DIR" != "" ]; then
	echo "third remote dir is not null, exit !"
	exit 1	
fi

if [ "$SECOND_REMOTE_DIR" != "" ]; then
	echo "Two level dir ftp upload, local file=$LOCALDIR/$LOCALFILE, remote file=/$TOP_REMOTE_DIR/$SECOND_REMOTE_DIR/$REMOTEFILE"

	$FTP_PATH -i -n $1 $2 <<END_SCRIPT
	user $3 $4
	bin
	passive
	mkdir $TOP_REMOTE_DIR
	cd $TOP_REMOTE_DIR
	mkdir $SECOND_REMOTE_DIR 
	cd $SECOND_REMOTE_DIR  
	lcd $LOCALDIR
	put $LOCALFILE 
	quit
END_SCRIPT
else
	if [ "$TOP_REMOTE_DIR" != "" ]; then
		echo "One level dir ftp upload, local file=$LOCALDIR/$LOCALFILE, remote file=/$TOP_REMOTE_DIR/$REMOTEFILE"

        	$FTP_PATH -i -n $1 $2 <<END_SCRIPT
        	user $3 $4
        	bin
        	passive
        	mkdir $TOP_REMOTE_DIR
        	cd $TOP_REMOTE_DIR
        	lcd $LOCALDIR
        	put $LOCALFILE 
        	quit
END_SCRIPT
	else
        echo "Root dir ftp upload, local file=$LOCALDIR/$LOCALFILE, remote file=$REMOTEFILE"
		
		$FTP_PATH -i -n $1 $2 <<END_SCRIPT
                user $3 $4
                bin
                passive
                lcd $LOCALDIR
                put $LOCALFILE 
                quit
END_SCRIPT
	fi
fi
exit 0
<span style="font-size:18px;">从源码中挖出一部分出来后,测试的结果还是比较理想的,移植的超级简单,这里做个记录:</span>
<span style="font-size:18px;">1、FtpClient.h</span>
<span style="font-size:18px;"></span><pre class="cpp" name="code">/*
 * FtpClient.h
 *
 *  Created on: Aug 23, 2014
 *      Author: xtank.nie@gmail.com
 */

#ifndef FTP_SOCKET_H_
#define FTP_SOCKET_H_

#include <iostream>
#include <string>

using std::string;

class CFtpClient
{
public:
    CFtpClient(void);
    ~CFtpClient(void);

public:
		//  Init("192.168.1.80", 21, "test", "test");
    int Init(const string &ftpServer, unsigned short port,
             const string &user, const string &password);
    
    //  根目录:  UploadFile "/tmp/sd0/test.jpg" "/test.jpg"
    //一级目录:  UploadFile "/tmp/sd0/test.jpg" "/pic/test.jpg"
    int UploadFile(const string &localPath, const string &remotePath);
    
    int Deinit(void);

private:
    int HookupServer(const string &ftpServer, unsigned short port = 21);
    int VerifyFtpUserPassword(const string &user, const string &password);
    int PassiveModeFileTransferConnect(void);
    int InteractiveMode(void);
    int GetFtpServerReply(int expecteof);
    int CommonCommand(const char *fmt, ...);
    int InitFileTransferConnect(void);
    void lcd(const char *dir);
    int FtpPutFile(const char *localFileName, const char *remoteFileName);
    FILE *DataConn(const char *lmode);
    bool HookupOK(void);
    int Makedir(const char *dir);
    int GotoDir(const char *dir);

private:
    FILE *m_commandRead, *m_commandWrite;
    int m_connected;
    string m_recvContent;
    int m_fileTransferSocket;
    int m_commandSocket;
    string m_localDir, m_remoteDir;
    bool m_init;
 };

#endif /* FTP_CLIENT_H_ */

 
 
//FtpClient.cpp
/*
 * FtpClient.cpp
 *
 *  Created on: Aug 23, 2014
 *      Author: xtank.nie@gmail.com
 */

#include <netinet/in.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/telnet.h>
#include <netdb.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <arpa/ftp.h>
#include <fcntl.h>

#include "FtpClient.h"

CFtpClient::CFtpClient(void):
m_commandRead(NULL),
m_commandWrite(NULL),
m_connected(0),
m_recvContent(""),
m_fileTransferSocket(-1),
m_commandSocket(-1),
m_init(0)
{
}

CFtpClient::~CFtpClient(void)
{
    Deinit();
}

int CFtpClient::Init(const string &ftpServer, unsigned short port,
                     const string &user, const string &password)
{
    if (m_init)
        return 0;

    int isHookup = -1, checkUserpwd = -1;
    if ((isHookup = HookupServer(ftpServer, port)) == 0)
    {
        checkUserpwd = VerifyFtpUserPassword(user, password);
        if (checkUserpwd == 0)
        {
            if (InteractiveMode() == 0)
            {
                m_init = true;
                fprintf(stderr, "ftp client init ok !\n");
                return 0;
            }
            else
                return -1;
        }
        else
        {
            fprintf(stderr, "FTP verify username or passwd error !\n");
            return -1;
        }
    }
    else
    {
        fprintf(stderr, "FTP HookupServer error !\n");
        return -1;
    }
    return 0;
}

int CFtpClient::HookupServer(const string &ftpServer, unsigned short port)
{
    struct hostent *hp = NULL;
    struct sockaddr_in hisctladdr, myctladdr;
    socklen_t len;
    char hostnamebuf[256] = {0};

    memset(&hisctladdr, 0, sizeof(hisctladdr));
    if (inet_aton(ftpServer.c_str(), &hisctladdr.sin_addr)) //IP address
    {
        hisctladdr.sin_family = AF_INET;
        strncpy(hostnamebuf, ftpServer.c_str(), sizeof(hostnamebuf));
        hostnamebuf[sizeof(hostnamebuf)-1]=0;
        fprintf(stderr, "ftp server ipaddr=%s\n", hostnamebuf);
    }
    else
    {
        hp = gethostbyname(ftpServer.c_str()); //domain name
        if (hp == NULL)
        {
            fprintf(stderr, "ftp: %s: ", ftpServer.c_str());
            herror((char *)NULL);
            return -1;
        }
        hisctladdr.sin_family = hp->h_addrtype;
        if (hp->h_length > (int)sizeof(hisctladdr.sin_addr))
            hp->h_length = sizeof(hisctladdr.sin_addr);
        memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], hp->h_length);
        strncpy(hostnamebuf, hp->h_name, sizeof(hostnamebuf));
        hostnamebuf[sizeof(hostnamebuf)-1] = 0;
        fprintf(stderr, "ftp server domain=%s(%s)\n", hostnamebuf, inet_ntoa(hisctladdr.sin_addr));
    }

    if ((m_commandSocket = socket(hisctladdr.sin_family, SOCK_STREAM, 0)) < 0)
    {
        perror("ftp: socket");
        return -1;
    }
    hisctladdr.sin_port = htons(port);
    int i = 0;
    while (connect(m_commandSocket, (struct sockaddr *)&hisctladdr, sizeof (hisctladdr)) < 0)
    {
        ++i;
        if (hp && hp->h_addr_list[i]) //下一个地址
        {
            fprintf(stderr, "ftp: connect to address %s: ", inet_ntoa(hisctladdr.sin_addr));
            hp->h_addr_list++;
            memcpy(&hisctladdr.sin_addr, hp->h_addr_list[0], hp->h_length);
            fprintf(stdout, "Trying %s...\n", inet_ntoa(hisctladdr.sin_addr));
            close(m_commandSocket);
            if ((m_commandSocket = socket(hisctladdr.sin_family, SOCK_STREAM, 0)) < 0)
            {
                perror("ftp: socket");
                return -1;
            }
            continue;
        }
        perror("ftp: connect");
        goto bad;
    }
    len = sizeof(myctladdr);
    memset(&myctladdr, 0x0, sizeof(struct sockaddr_in));
    if (getsockname(m_commandSocket, (struct sockaddr *)&myctladdr, &len) < 0)
    {
        perror("ftp: getsockname");
        goto bad;
    }
    else
    {
        printf("client socket info: socket=%d, family=%d, ip=%s, port=%u\n",
                m_commandSocket,
                myctladdr.sin_family,
                inet_ntoa(myctladdr.sin_addr),
                ntohs(myctladdr.sin_port));
    }

    m_commandRead = fdopen(m_commandSocket, "r");
    m_commandWrite = fdopen(m_commandSocket, "w");
    if (m_commandRead == NULL || m_commandWrite == NULL)
    {
        fprintf(stderr, "ftp: fdopen failed.\n");
        goto bad;
    }
    if (HookupOK())
    {
        printf("ftpClient Connected to %s.\n", hostnamebuf);
        m_connected = 1;
        return 0;
    }


bad:
    if (m_commandRead)
    {
        (void) fclose(m_commandRead);
        m_commandRead = NULL;
    }
    if (m_commandWrite)
    {
        (void) fclose(m_commandWrite);
        m_commandWrite = NULL;
    }
    close(m_commandSocket); m_commandSocket = -1;
    m_connected = 0;
    m_init = false;
    return -1;
}

int CFtpClient::GetFtpServerReply(int expecteof)
{
FRONT:
    char c;
    m_recvContent = "";
 	for (;;)
 	{
		while ((c = getc(m_commandRead)) != '\n')
		{
			if (c == IAC)
			{   /* handle telnet commands */
			    printf("------------IAC");
#if 0
				switch (c = getc(m_commandRead))
				{
				case WILL:
				case WONT:
					c = getc(m_commandRead);
					fprintf(m_commandWrite, "%c%c%c", IAC, DONT, c);
					(void) fflush(m_commandWrite);
					break;
				case DO:
				case DONT:
					c = getc(m_commandRead);
					fprintf(m_commandWrite, "%c%c%c", IAC, WONT, c);
					(void) fflush(m_commandWrite);
					break;
				default:
					break;
				}
#endif
				continue;
			}

			if (c == EOF)
			{
				if (expecteof)
				{
					printf("getc char is EOF.\n");
					return (0);
				}
                printf("421 Service not available, remote server has closed connection\n");
                (void) fflush(stdout);
                m_recvContent.clear();
 				return -1;
 			}
            m_recvContent.append(1, c);
         }
         break;
    }
    if (m_recvContent[3] != ' ')
    {
        m_recvContent.clear();
        goto FRONT;
    }
    //printf("m_recvContent: %s\n", m_recvContent.c_str());
    return 0;
}

int CFtpClient::VerifyFtpUserPassword(const string &user, const string &password)
{
    if (m_connected == 1)
    {
        CommonCommand("USER %s", user.c_str());
        if (m_recvContent.find("331") != m_recvContent.npos)
        {
            CommonCommand("PASS %s", password.c_str());
            if (m_recvContent.find("230") != m_recvContent.npos)
            {
                fprintf(stderr, "login success !\n");
                return 0;
            }
        }
    }
    return -1;
}


int CFtpClient::CommonCommand(const char *fmt, ...)
{
	va_list ap;
	if (m_commandWrite == NULL)
	{
		perror ("No control connection for command");
		return (-1);
	}

	va_start(ap, fmt);
	vfprintf(m_commandWrite, fmt, ap);
	va_end(ap);
	fprintf(m_commandWrite, "\r\n");
	(void) fflush(m_commandWrite);
	return GetFtpServerReply(!strcmp(fmt, "QUIT"));
}

int CFtpClient::PassiveModeFileTransferConnect(void)
{
    CommonCommand("PASV");
    if (m_recvContent.find("227") != m_recvContent.npos) //被动模式
    {
        InitFileTransferConnect();
        return 0;
    }
    return -1;
}

int CFtpClient::InteractiveMode(void)
{
    CommonCommand("TYPE I");
    if (m_recvContent.find("200") != m_recvContent.npos)
        return 0;
    return -1;
}

int CFtpClient::InitFileTransferConnect(void)
{
	int on = 1;
	u_long a1,a2,a3,a4,p1,p2;
    struct sockaddr_in data_addr;

	if (m_fileTransferSocket == -1)
	{
		m_fileTransferSocket = socket(AF_INET, SOCK_STREAM, 0);
		if (m_fileTransferSocket < 0)
		{
			perror("ftp: socket");
			return(-1);
		}
	    //printf("m_fileTransferSocket=%d\n", m_fileTransferSocket);

		//"227 Entering Passive Mode (192,168,1,219,4,22)"
		//被动模式下服务端告诉客户端自己已经开启了那个端口进行监听就绪,客户端可以进行connect
        int start = m_recvContent.find('(');
        int end = m_recvContent.find(')');
        string t_string = "";
        t_string.assign(m_recvContent, start+1, end-start-1);
        //std::cout << "t_string=" << t_string << std::endl;
		if (sscanf(t_string.c_str(),"%ld,%ld,%ld,%ld,%ld,%ld",
			   &a1,&a2,&a3,&a4,&p1,&p2)
		    != 6)
		{
			printf("Passive mode address scan failure. Shouldn't happen!\n");
			return(-1);
		}

		data_addr.sin_family = AF_INET;
		data_addr.sin_addr.s_addr = htonl((a1 << 24) | (a2 << 16) | (a3 << 8) | a4);
		data_addr.sin_port = htons((p1 << 8) | p2);

		if (connect(m_fileTransferSocket, (struct sockaddr *) &data_addr, sizeof(data_addr))<0)
		{
			perror("ftp: connect");
			close(m_fileTransferSocket);
			m_fileTransferSocket = -1;
			return(-1);
		}
		return(0);
	}
	return 0;
}

int CFtpClient::UploadFile(const string &localPath, const string &remotePath)
{
    string local_dir, local_file;
    string remote_file, remote_dir;
    int local_end_pos = localPath.rfind('/');
    int remote_end_pos = remotePath.rfind('/');

    if (!m_init)
        return -1;

    local_dir.assign(localPath, 0, local_end_pos); //
    local_file.assign(localPath, local_end_pos+1, localPath.length());
    remote_dir.assign(remotePath, 0, remote_end_pos+1);
    remote_file.assign(remotePath, remote_end_pos+1, remotePath.length()); //
#if 0
    fprintf(stderr, "local dir=%s, local file=%s\n", local_dir.c_str(), local_file.c_str());
    fprintf(stderr, "remote dir=%s, remote file=%s\n", remote_dir.c_str(), remote_file.c_str());
#endif

    if (m_localDir != local_dir)
    {
        lcd(local_dir.c_str());
        m_localDir = local_dir;
    }
    if (m_remoteDir != remote_dir)
    {
        m_remoteDir = remote_dir;
        unsigned int line_pos = 0;
        while (1)
        {
            line_pos = remote_dir.find('/', line_pos);
            if (line_pos != remote_dir.npos)
            {
                unsigned int line_pos_tmp = remote_dir.find('/', line_pos+1);
                if (line_pos_tmp != remote_dir.npos)
                {
                    string sub_str = remote_dir.substr(line_pos+1, line_pos_tmp-line_pos-1);
                    line_pos = line_pos_tmp;
                    Makedir(sub_str.c_str());
                    GotoDir(sub_str.c_str());
                }
                else
                    break;
            }
            else
                break;
        }
    }
    PassiveModeFileTransferConnect(); //开启被动模式传输
    FtpPutFile(local_file.c_str(), remote_file.c_str());
    if (m_recvContent.find("226") != m_recvContent.npos)
        return 0;
    else
    {
        fprintf(stderr, "file transferr error !\n");
        return -1;
    }
    return -1;
}

int CFtpClient::Makedir(const char *dir)
{
    CommonCommand("MKD %s", dir);
    if (m_recvContent.find("500") != m_recvContent.npos)
        CommonCommand("XMKD %s", dir);

    if (m_recvContent.find("257") == m_recvContent.npos)     //路径名建立
    {
        if (m_recvContent.find("550") == m_recvContent.npos) //已经存在
            return 0;
        else
            return -1;
    }
    else
        return 0;
}

int CFtpClient::GotoDir(const char *dir)
{
    CommonCommand("CWD %s", dir);
    if (m_recvContent.find("500") != m_recvContent.npos) //不识别指令
        CommonCommand("XCWD %s", dir);

    if (m_recvContent.find("250") == m_recvContent.npos)
        return -1;
    else
        return 0;       //进入目录
}

void CFtpClient::lcd(const char *dir)
{
    char buf[1024] = {0};
	if (!dir)
	{
		return;
	}
	if (chdir(dir) < 0)
	{
		fprintf(stderr, "local: %s: %s\n", dir, strerror(errno));
		return;
	}
	if (!getcwd(buf, sizeof(buf)))
	{
	    if (errno==ERANGE) strcpy(buf, "<too long>");
	    else strcpy(buf, "???");
	}
	//printf("Local directory now %s\n", buf);
    return;
}


FILE *CFtpClient::DataConn(const char *lmode)
{
    return fdopen(m_fileTransferSocket, lmode);
}

int CFtpClient::FtpPutFile(const char *localFileName, const char *remoteFileName)
{
    struct stat st;
    register int c, d;
    FILE *volatile fin, *volatile dout = 0;
    volatile long bytes = 0;
    char buf[1024], *bufp;

    fin = fopen(localFileName, "r");
    if (fin == NULL)
    {
        fprintf(stderr, "local: %s: %s\n", localFileName, strerror(errno));
        return -1;
    }
    if (fstat(fileno(fin), &st) < 0 || (st.st_mode&S_IFMT) != S_IFREG)
    {
        fprintf(stdout, "%s: not a plain file.\n", localFileName);
        fclose(fin);
        return -1;
    }
	//CommonCommand("%s %s", "APPE", remoteFileName);
	CommonCommand("%s %s", "STOR", remoteFileName);
    dout = DataConn("w");
    if (dout == NULL)
    {
        perror("fdopen:");
        return (-1);
    }
    while ((c = read(fileno(fin), buf, sizeof (buf))) > 0)
    {
        bytes += c;
        for (bufp = buf; c > 0; c -= d, bufp += d)
        {
            if ((d = write(fileno(dout), bufp, c)) <= 0)
            {
                perror("write");
                break;
            }
        }
    }
    fclose(fin); fclose(dout);
    m_fileTransferSocket = -1;
    GetFtpServerReply(0);
    if (c < 0)
    {
        fprintf(stderr, "local: %s: %s\n", localFileName, strerror(errno));
        return -1;
    }

#if 0
    if (bytes > 0)
        printf("send byte=%ld\n", bytes);
#endif
     return 0;
}

bool CFtpClient::HookupOK(void)
{
    GetFtpServerReply(0);
    if (m_recvContent.find("220") != m_recvContent.npos) //服务就绪
        return true;
    return false;
}

int CFtpClient::Deinit(void)
{
    if (!m_init)
        return 0;

    if (m_commandRead != NULL)
    {
        fclose(m_commandRead);
        m_commandRead = NULL;
    }
    if (m_commandWrite != NULL)
    {
        fclose(m_commandWrite);
        m_commandWrite = NULL;
    }
    if (m_fileTransferSocket != -1)
    {
        close(m_fileTransferSocket);
        m_fileTransferSocket = -1;
    }
    if (m_commandSocket != -1)
    {
        close(m_commandSocket);
        m_commandSocket = -1;
    }
    m_connected = 0;
    m_init = false;
    m_recvContent.clear();
    m_remoteDir.clear();
    m_localDir.clear();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值