欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

FTP之Linux与windows文件传输

程序员文章站 2022-07-12 12:55:30
...

一.概述

    为实现存储数据文件定时的、且在不同操作系统环境下的进行传输共享,采用FTP文件传输协议进行数据文件的传输。本次尝试采用免费开源的FTP工具软件FileZilla,分为客户端和服务端两种版本,支持上传,下载等功能。由于项目的数据文件是在Linux下产生的,且需要将数据数据传输到windows下,因此本次使用FileZilla Server版本作为FTP的服务器,FTP的客户端编程实现。FTP服务器运行在Windows环境下,只需配置用户名密码,及存储文件目录,由于会有多个节点的数据回传到服务器下,配置文件目录时,需手动创建多个节点对应的文件夹;FTP的客户端需运行在Linux下,实时监控新产生的数据文件并上传到服务器,以供其他应用程序使用数据文件。

二.FileZilla

       FileZilla是一个免费开源的FTP软件,分为客户端版本和服务器版本,具备所有的FTP软件功能。可控性、有条理的界面和管理多站点的简化方式使得Filezilla客户端版成为一个方便高效的FTP客户端工具,而FileZilla Server则是一个小巧并且可靠的支持FTP&SFTPFTP服务器软件。

FileZilla server下载:百度搜索即可找到下载官网。或者以下官网链接:

https://filezilla-project.org/download.php?type=server

使用的版本是FileZilla Server 0.9.60.2,只支持Windows788.110操作系统,32位和64位都可以使用。

下载完成后,如图所示:

FTP之Linux与windows文件传输

直接双击安装,一直点击下一步即可,如若不明白,也可以网上查找安装教程。

安装完成后,打开FileZilla Server,出现如下界面:

 FTP之Linux与windows文件传输

点击connect,登录服务。点击“Edit->Users,如下图

 FTP之Linux与windows文件传输

这里添加一个用户luotest,密码设置为123456。如图所示

 FTP之Linux与windows文件传输

接下来,创建共享文件目录,即FTP服务器的文件存储目录,在D盘新建一个文件夹FTPTEST作为共享目录,在该文件下新创建Node1Node2Node3文件夹作为每个FTP客户端传送文件的存储目录,如图所示:

 FTP之Linux与windows文件传输

回到FileZilla Server刚刚的配置界面,选择“shared folders”,如图所示:

 FTP之Linux与windows文件传输

添加我们刚刚在D盘创建的共享目录FTPTEST

 FTP之Linux与windows文件传输

点击OK,我们服务器配置就基本上完成了。

三.FTP客户端

    客户端需要运行在Linux下,FTP客户端的编程涉及到的概念和协议这里不多说,推荐链接如下:

Linux下用C编写客户端:

http://lib.csdn.net/article/linux/33415

https://www.ibm.com/developerworks/cn/linux/l-cn-socketftp/

Linux下用C++编写客户端:

http://blog.csdn.net/swartz_lubel/article/details/57115820

参考上面的链接,直接运行作者写的程序,总会出现各种问题,可能各种环境及配置有所区别,这让人很是烦躁,最后还是按照自己的逻辑编写代码,毕竟需求不一样,代码组织逻辑结构就有所区别,我也是初学者,就简单实现了项目的需求功能,主要实现FTP服务器的连接登录、文件监视、文件上传及配置属性读取。

贴出来供大家参考。

main.cpp:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <dirent.h>
#include <sys/time.h>
#include <ctime>
#include <pthread.h>
#include "configftp.h"
#include <vector>
using namespace std;



int TransferConnect(char *ip, int port);      //建立文件传输连接
int ftp_up(int datasock, char * filepath);    //上传文件
void getTime(char p[]);                             //获取当前时间
void ShowMessage(char *s);                     //显示接收信息

char * filepath;           //数据文件路径
char *filetype;            //数据文件类型
char * serverip;          //FTP服务器地址
int port = 21;            //FTP服务器端口
char *username;       //用户名
char *password;        //密码
char *nodename;     //节点名


char t[50];                      //保存时间
vector <char*>filelist;    //需上传的数据文件

//数据传输端口解析
int strtosrv(char *str)
{
   int addr[6];



    //sscanf函数是从一个字符串中读进与制定格式相符的数据,可以和sprintf来对应。这里用到了一点正则表达        式,用来匹配相应的字符结果。
   sscanf(str,"%*[^(](%d,%d,%d,%d,%d,%d)",&addr[0],&addr[1],&addr[2],&addr[3],&addr[4],&addr[5]);  



   int port = addr[4]*256 + addr[5];
   return port;
}




//获取文件扩展名
void get_extension(const char *file_name,char *extension)
{
    int i=0,length;
    length=strlen(file_name);
    while(file_name[i])
    {
        if(file_name[i]=='.')
        break;
        i++;
    }
    if(i<length)
    strcpy(extension,file_name+i+1);
    else
    strcpy(extension,"\0");
}


//获取数据文件
bool GetAllFiles(const char * dir_name)
{
    if(NULL == dir_name)
    {
        cout<<"dir_name is null!"<<endl;
        return false;
    }

    //check if dir_name is a valid dir
    struct stat s;
    lstat(dir_name,&s);
    if(!S_ISDIR(s.st_mode))
    {
        cout<<"dir_name is not a valid directory"<<endl;
        return false;
    }


    struct dirent *filename;
    DIR *dir;
    dir = opendir(dir_name);
    if(NULL == dir)
    {
        cout<<"Can not open dir "<<dir_name<<endl;
        return false;
    }
    
    char fileextension[10]; //保存查找到的文件扩展名
    bool ExsitFile = false; 

    while((filename = readdir(dir))!= NULL)
    {
        if(strcmp(filename->d_name,".") == 0 ||strcmp(filename->d_name,"..") == 0  )
            continue;
        get_extension(filename->d_name,fileextension);
        if(strcmp(fileextension,filetype)==0) 
        {   

            //如果在数据文件路径查找到数据文件类型与配置文件要求的类型一致则加入文件集合filelist           filelist.push_back(filename->d_name);
           ExsitFile = true;   //标志有该类型的文件需要上传
        }
    }

    closedir(dir);
    return ExsitFile;
}



void getTime(char p[]) //获取时间
{

     time_t timestamp;
     struct tm *time_tm;
     time(×tamp);
     time_tm = localtime(×tamp);
     char strtime[50];
     strftime(strtime, sizeof(strtime), "%Y-%m-%d %H:%M:%S", time_tm);

     memccpy(p,strtime,0,strlen(strtime));
}



void ShowMessage(char *s)//显示提示信息
{
    getTime(t);
    cout<<t<<"\t"<<s<<endl;
}



void *thread(void *ptr)  //线程执行函数,循环执行文件的获取,及上传文件操作
{
    pthread_detach(pthread_self());  

    bool findFile=false;
    int control_sock;
    int data_sock;

    char read_buf[512]={0};
    int read_len = 0;

    while(true)
    {
        findFile = GetAllFiles(filepath);
        if(findFile)
        {
            control_sock = TransferConnect(serverip,port);
            if(control_sock < 0)
            {
                printf("Can not connect target commuputer!!!");
                exit(0);
            }

            /*220  receive welcome message from server*/
            read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
            if(read_len != 0)
            {
                if(strncmp(read_buf,"220",3)==0)
                {
                    /*send user name to server*/
                    //输入用户名
                     char userbuf[100];
                     sprintf(userbuf, "USER %s\r\n", username);
                    if(write(control_sock,userbuf,strlen(userbuf))<0)
                    {
                       printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
                       exit(0);
                    }
                }
                memset(read_buf,0,read_len);
            }

            read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
            if(read_len != 0)
            {
                 if(strncmp(read_buf,"331",3)==0)
                 {
                     //command "PASS password\r\n"
                     //输入密码
                     char passbuf[100];
                     sprintf(passbuf, "PASS %s\r\n", password);
                     write(control_sock, passbuf, strlen(passbuf));
                 }
                 memset(read_buf,0,read_len);
            }

            bool isLogin= false;
            read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
            if(read_len != 0)
            {
                if(strncmp(read_buf,"230",3)==0)
                {
                    //login success
                    isLogin = true;                    
                }
                else
                    ShowMessage(read_buf);
                memset(read_buf,0,read_len);
            }

            if(isLogin)  //使用用户名和密码登录FTP服务器成功
            {
                //改变工作节点目录,将文件上传到该节点目录
                char cwdbuf[100];
                sprintf(cwdbuf,"CWD %s\r\n",nodename);
                write(control_sock,cwdbuf,strlen(cwdbuf));
                read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
                if(read_len != 0)
                {
                    if(strncmp(read_buf,"250",3)!=0)
                    {
                        ShowMessage(read_buf);
                    }
                    memset(read_buf,0,read_len);
                }

                for(int i = 0;i<filelist.size(); i++)
                {
                    //让服务器进入被动模式,在数据端口监听
                    char pabuf[]={"PASV\r\n"};
                    write(control_sock, pabuf, strlen(pabuf));
                    read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
                    if(read_len != 0)
                    {
                        if(strncmp(read_buf,"227",3)==0)
                        {
                            //data port
                            int portdata = strtosrv(read_buf);
                            //create data channel
                            data_sock = TransferConnect(serverip,portdata);
                        }
                        else
                            ShowMessage(read_buf);
                        memset(read_buf,0,read_len);
                    }

                    //上传文件名
                    char upcmd[]={0};
                    sprintf(upcmd,"STOR %s\r\n",filelist[i]);
                    write(control_sock, upcmd, strlen(upcmd));
                    read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
                    if(read_len != 0)
                    {
                        if(strncmp(read_buf,"150",3)!=0)
                        {
                            ShowMessage(read_buf);
                        }
                        memset(read_buf,0,read_len);
                    }

                    char filepath1[256]={0};
                    char *ps = "/";
                    char *path = strcat(filepath1,filepath);
                    path = strcat(filepath1,ps);
                    path = strcat(filepath1,filelist[i]);

                    ftp_up(data_sock,path); //数据文件上传
                   
                    close(data_sock); //关闭数据套接字

                    read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
                    if(read_len != 0)
                    {
                        if(strncmp(read_buf,"226",3)==0)
                        {
                            //上传完成后,删除本地的月报文件
                            remove(path);
                            ShowMessage(read_buf);
                        }
                        else
                            ShowMessage(read_buf);
                        memset(read_buf,0,read_len);
                    }
                }
                //清空文件列表
                filelist.clear();
                //主动退出与FTP服务的连接,如果不主动退出,也会自动超时退出
                char quitcmd[]={"QUIT\r\n"};
                write(control_sock, quitcmd, strlen(quitcmd));
                read_len = recv(control_sock,read_buf,sizeof(read_buf),0);
                if(read_len != 0)
                {
                     if(strncmp(read_buf,"221",3)==0)
                     {
                         //cout<<read_buf<<endl;
                     }
                     memset(read_buf,0,read_len);
                }

               close(control_sock);
            }

           close(data_sock);
           close(control_sock);
        }
        sleep(5);//每隔5秒查询一次是否有新的数据文件产生
    }
}
int main()
{
    char t[50]={0};
    getTime(t);
    cout << "Hello ftp! " << t << endl;
    //获取配置文件
    char buf[MAX_PATH];
    char g_szConfigPath[MAX_PATH]; //记录当前配置文件路径
    memset(buf,0,sizeof(buf));
    GetCurrentPath(buf,ConfigPath); //获取当前配置文件路径
    strcpy(g_szConfigPath,buf);

    username = (char*)malloc(100*sizeof(char));
    password = (char*)malloc(100*sizeof(char));
    filepath = (char*)malloc(100*sizeof(char));
    filetype = (char*)malloc(100*sizeof(char));
    serverip = (char*)malloc(100*sizeof(char));
    nodename = (char*)malloc(100*sizeof(char));


    //从配置文件中获取各属性值

    //用户名

    strcpy(username,GetIniKeyString("ftpconfig","UserName",g_szConfigPath));
    if(strcmp(username,"")==0)
    {
        cout<<"Get User name is error!"<<endl;
        exit(0);
    }
    //密码
    strcpy(password,GetIniKeyString("ftpconfig","Password",g_szConfigPath));
    if(strcmp(password,"")==0)
    {
        cout<<"Get Password is error!"<<endl;
        exit(0);
    }
    //文件路径
    strcpy(filepath, GetIniKeyString("ftpconfig","FilePath",g_szConfigPath));
    if(strcmp(filepath,"")==0)
    {
        cout<<"Get file path is error!"<<endl;
        exit(0);
    }
    //文件类型
    strcpy(filetype,GetIniKeyString("ftpconfig","FileType",g_szConfigPath));
    if(strcmp(filetype,"")==0)
    {
        cout<<"Get file type is error!"<<endl;
        exit(0);
    }
    //FTP服务器端口
    port = GetIniKeyInt("ftpconfig","TargetPort",g_szConfigPath);

    //FTP服务器IP

    strcpy(serverip,GetIniKeyString("ftpconfig","TargetIP",g_szConfigPath));
    if(strcmp(serverip,"")==0)
    {
        cout<<"Target IP is error"<<endl;
        exit(0);
    }
    //FTP服务器文件保存路径
    strcpy(nodename,GetIniKeyString("ftpconfig","NodeName",g_szConfigPath));
    if(strcmp(nodename,"")==0)
    {
        cout<<"Node name is error"<<endl;
        exit(0);
    }

    //显示相关配置信息
    cout<<"用户名: "<<username<<endl;
    cout<<"密码: "<<password<<endl;
    cout<<"存储文件路径: "<<filepath<<endl;
    cout<<"文件类型: "<<filetype<<endl;
    cout<<"FTP服务器IP: "<<serverip<<endl;
    cout<<"FTP服务器端口: "<<port<<endl;
    cout<<"节点名: "<<nodename<<endl;

   //创建线程
    pthread_t id;
    int ret = pthread_create(&id,NULL,thread,NULL);
    if(ret != 0)
    {
        cout<<"Create Thread error!!!"<<endl;
        return -1;
    }

    pthread_join(id,NULL);

    free(username);
    free(password);
    free(filepath);
    free(filetype);
    free(nodename);
    return 0;
}

//上传文件
int ftp_up(int datasock, char * filepath)
{
      int handle =open(filepath,O_RDWR);
      int nread;
      if(handle == -1)
          return -1;

      char rbuf1[512]={0};
      for(;;)
      {
          if((nread = read(handle,rbuf1,512)) < 0)
          {
               printf("read error!");
          }
          else if(nread == 0)
             break;
          //if(write(STDOUT_FILENO,rbuf1,nread) != nread)
               //printf("send error!");
          if(write(datasock,rbuf1,nread) != nread)
               printf("send error!");
      }
      if(close(handle) < 0)
           printf("close error\n");
      return 0;
}

/// <summary>
/// 建立tcp连接
/// </summary>
/// <param name="ip">服务地址</param>
/// <param name="port">端口</param>
/// <returns>套接字描述符</returns>
int TransferConnect(char *ip, int port)
{
    struct sockaddr_in dataSocket;
    memset(&dataSocket,0, sizeof(struct sockaddr_in));

    /*init socket*/
    int data_sock =socket(AF_INET,SOCK_STREAM,0);
    if(data_sock < 0)
    {
       printf("socket error\n");
       return -1;
    }

    dataSocket.sin_family = AF_INET;
    dataSocket.sin_port = htons(port);
    dataSocket.sin_addr.s_addr = inet_addr(ip);

    /*connect to server*/

    if(connect(data_sock, (struct sockaddr*)&dataSocket, sizeof(struct sockaddr)) == -1)
    {
        perror("connect");
        close(data_sock);
        return -1;
    }
    /*back handle of data channel*/
    return data_sock;
}
configftp.h:
#ifndef CONFIGFTP
#define CONFIGFTP

#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define ConfigPath "config.ini"

#define  MAX_PATH 260


//获取当前程序目录
int GetCurrentPath(char buf[],char *pFileName);

//从INI文件读取字符串类型数据
char *GetIniKeyString(char *title,char *key,char *filename);

//从INI文件读取整类型数据
int GetIniKeyInt(char *title,char *key,char *filename);

#endif // CONFIGFTP

 configftp.cpp:

#include "configftp.h"

int GetCurrentPath(char buf[],char *pFileName)//获取当前程序运行环境路径
{
//#ifdef WIN32
//    GetModuleFileName(NULL,buf,MAX_PATH);
//#else
    char pidfile[64];
    int bytes;
    int fd;

    sprintf(pidfile, "/proc/%d/cmdline", getpid());

    fd = open(pidfile, O_RDONLY, 0);
    bytes = read(fd, buf, 256);
    close(fd);
    buf[MAX_PATH] = '\0';

//#endif
    char * p = &buf[strlen(buf)];
    do
    {
        *p = '\0';
        p--;
//#ifdef WIN32
//    } while( '\\' != *p );
//#else
    } while( '/' != *p );
//#endif

    p++;

    //配置文件目录
    memcpy(p,pFileName,strlen(pFileName));
    return 0;
}

//从INI文件读取字符串类型数据
char *GetIniKeyString(char *title,char *key,char *filename)
{
    FILE *fp;
    char szLine[1024];
    static char tmpstr[1024];
    int rtnval;
    int i = 0;
    int flag = 0;
    char *tmp;

    if((fp = fopen(filename, "r")) == NULL)
    {
        printf("have   no   such   file \n");
        return "";
    }
    while(!feof(fp))
    {
        rtnval = fgetc(fp);
        if(rtnval == EOF)
        {
            break;
        }
        else
        {
            szLine[i++] = rtnval;
        }
        if(rtnval == '\n')
        {
#ifndef WIN32

#endif
            szLine[i] = '\n';
            szLine[--i] = '\0';

            i = 0;
            tmp = strchr(szLine, '=');

            if(( tmp != NULL )&&(flag == 1))
            {
                if(strstr(szLine,key)!=NULL)
                {
                    //注释行
                    if ('#' == szLine[0])
                    {
                    }
                    else if ( '\/' == szLine[0] && '\/' == szLine[1] )
                    {

                    }
                    else
                    {
                        //找打key对应变量
                        strcpy(tmpstr,tmp+1);
                        fclose(fp);
                        return tmpstr;
                    }
                }
            }
            else
            {
                strcpy(tmpstr,"[");
                strcat(tmpstr,title);
                strcat(tmpstr,"]");
                if( strncmp(tmpstr,szLine,strlen(tmpstr)) == 0 )
                {
                    //找到title
                    flag = 1;
                }
            }
        }
    }
    fclose(fp);
    return "";
}

//从INI文件读取整类型数据
int GetIniKeyInt(char *title,char *key,char *filename)
{
    return atoi(GetIniKeyString(title,key,filename));
}

配置属性文件config.ini如下几个参数:

[ftpconfig]
#用户名
UserName=luotest

#密码
Password=123456

#本机数据文件路径
FilePath=/home/luo/Upfile

#数据文件类型后缀名
FileType=txt

#FTP服务器IP
TargetIP=192.168.1.95

#FTP服务器端口
TargetPort=21

#节点名
NodeName=Node1

用户名密码就是配置FileZilla Server时我们添加的用户名及密码,节点名就是我们产生的数据文件需要上传后保存的文件路径,这样就可以区别各个节点的数据。