FTP之Linux与windows文件传输
一.概述
为实现存储数据文件定时的、且在不同操作系统环境下的进行传输共享,采用FTP文件传输协议进行数据文件的传输。本次尝试采用免费开源的FTP工具软件FileZilla,分为客户端和服务端两种版本,支持上传,下载等功能。由于项目的数据文件是在Linux下产生的,且需要将数据数据传输到windows下,因此本次使用FileZilla Server版本作为FTP的服务器,FTP的客户端编程实现。FTP服务器运行在Windows环境下,只需配置用户名密码,及存储文件目录,由于会有多个节点的数据回传到服务器下,配置文件目录时,需手动创建多个节点对应的文件夹;FTP的客户端需运行在Linux下,实时监控新产生的数据文件并上传到服务器,以供其他应用程序使用数据文件。
二.FileZilla
FileZilla是一个免费开源的FTP软件,分为客户端版本和服务器版本,具备所有的FTP软件功能。可控性、有条理的界面和管理多站点的简化方式使得Filezilla客户端版成为一个方便高效的FTP客户端工具,而FileZilla Server则是一个小巧并且可靠的支持FTP&SFTP的FTP服务器软件。
FileZilla server下载:百度搜索即可找到下载官网。或者以下官网链接:
https://filezilla-project.org/download.php?type=server
使用的版本是FileZilla Server 0.9.60.2,只支持Windows7、8、8.1、10操作系统,32位和64位都可以使用。
下载完成后,如图所示:
直接双击安装,一直点击下一步即可,如若不明白,也可以网上查找安装教程。
安装完成后,打开FileZilla Server,出现如下界面:
点击connect,登录服务。点击“Edit”->“Users”,如下图
这里添加一个用户luotest,密码设置为123456。如图所示
接下来,创建共享文件目录,即FTP服务器的文件存储目录,在D盘新建一个文件夹FTPTEST作为共享目录,在该文件下新创建Node1,Node2,Node3文件夹作为每个FTP客户端传送文件的存储目录,如图所示:
回到FileZilla Server刚刚的配置界面,选择“shared folders”,如图所示:
添加我们刚刚在D盘创建的共享目录FTPTEST。
点击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时我们添加的用户名及密码,节点名就是我们产生的数据文件需要上传后保存的文件路径,这样就可以区别各个节点的数据。