www.pudn.com > ftp.rar > zhuftp.cpp


#include "zhuftp.h"

//#define TESTZHUFTP 
#ifdef TESTZHUFTP
#include 
#endif
#if 1
#define Dprintf printf
#else
#define Dprintf 
#endif

FtpZhu::FtpZhu()
:FtpStatus(FTP_DISCONNECTED),CmdSocket(0),Timeout(20),lasttime(0)
{

}
FtpZhu::~FtpZhu()
{
	if(CmdSocket!=0)
	{
		close(CmdSocket);
	}
}
/**检查是否超时.
flag:更新时间标志,0查询时间,1,更新时间.
返回:0超时,1正常.
*/
int FtpZhu::CheckTime(int flag)
{
	time_t ctime;
	if(lasttime==0 || flag==1)
	{
		lasttime=time(NULL);
		return 1;
	}
	if(flag==0)
	{
		ctime=time(NULL);
		if(ctime-lasttime>Timeout)
			return 0;
	}
	return 1;
}
/**设置超时时间,单位秒,初始默认为20秒
*/
void FtpZhu::SetTimeout(int interval)
{
	Timeout=interval;
}
/**发送FTP命令.
返回:ftp服务器的返回码.
参数:sockfd连接ftp的socket.
    cmd发送的命令字符串.
*/
int FtpZhu::SendFtpCommand(int sockfd,const char*cmd)
{
	int ret;
	char val[4];
	if((ret=send(sockfd,cmd,strlen(cmd),0))==-1)
	{
		perror("ftp send command");
	}
	Dprintf("CLIENT:: %s\n",cmd);
	//receive data from server
	memset(recvbuffer,0,sizeof(recvbuffer));
	if((ret=recv(sockfd,recvbuffer,sizeof(recvbuffer),0))==-1)
		perror("ftp recv command return");
	Dprintf("SERVER:: %s\n",recvbuffer);
	strncpy(val,recvbuffer,3);
	val[3]='\0';
	ret=atoi(val);
	Dprintf("Ret is %d\n",ret);
	return ret;
}
/**登录ftp服务器.
ServerIp:服务器IP,如192.168.0.59
uname:用户名
password:密码
返回:ftp服务器的返回码.
*/
int FtpZhu::ConnectServer(const char *ServerIp,const char*uname,const char*password)
{
	int ret;
	char ftpcmd[64];
	if((CmdSocket=socket (AF_INET,SOCK_STREAM,0))<0)
	{
		perror("ftp socket connect");
		return -1;
	}
	//fill struct sockaddr_in
	bzero(&cmdaddr,sizeof(cmdaddr));
	cmdaddr.sin_family=AF_INET;
	cmdaddr.sin_port=htons(21);
	cmdaddr.sin_addr.s_addr=inet_addr(ServerIp);
	//try connecting
	if (connect(CmdSocket,(struct sockaddr*)&cmdaddr,sizeof(cmdaddr))<0)
	{
		perror("connect");
		return -1;
	}
	//接收欢迎消息
	if((recv(CmdSocket,recvbuffer,sizeof(recvbuffer),0))==-1)
		perror("Connect Message Failed!");
	else
		Dprintf("SERVER:: %s\n",recvbuffer);
	sprintf(ftpcmd,"USER %s\r\n",uname);
	ret=SendFtpCommand(CmdSocket,ftpcmd);
	sprintf(ftpcmd,"PASS %s\r\n",password);
	ret=SendFtpCommand(CmdSocket,ftpcmd);
	sprintf(ftpcmd,"TYPE I\r\n");
	ret=SendFtpCommand(CmdSocket,ftpcmd);
	FtpStatus=FTP_CONECTED;
	return 0;
}
/**通过Ftp获取文件.
  	支持断点续传,目前尚未支持超时控制.可由外部信号中断.
	remotepath:远程ftp服务器目录
	localpath:本机目录
	filename:要下载的文件名.
	返回:0成功,-1失败.
*/
int FtpZhu::GetFile(const char *remotepath,const char *localpath,const char*filename)
{
	int ret,datasocket,dataport,recvlen,filesize;
	struct sockaddr_in data_addr;
	char databuf[1024],cmdbuf[128],*val;
	char remotename[128],localname[128],*tmpbuf;
	FILE *fp;
	struct stat files;
	int receiveend;
	
	//检测ftp必须连接上服务器
	if(FtpStatus != FTP_CONECTED)
		return -1;
	//检查本地文件是否已经存在,如果不存在则创建,如果无法创建将返回错误
	sprintf(localname,"%s/%s",localpath, filename);
	if ((ret = stat(localname, &files)) == -1)
		filesize=0;
	else
		filesize=files.st_size;
	if((fp=fopen(localname,"a"))==NULL)
	{
		perror("Open local file failed!");
		return -1;
	}
	fseek(fp,0,SEEK_END);
	//获得数据端口
	ret=SendFtpCommand(CmdSocket,"PASV\r\n");
	if (ret != 227)
	    return -1;
	val=strtok(recvbuffer,",");
	for(int i=0;i<4;i++)
		val=strtok(NULL,",");
	dataport=atoi(val);
	val=strtok(NULL,")");
	dataport=dataport*256+atoi(val);
	//Dprintf("data port is %d\n",dataport);
	if((datasocket=socket (AF_INET,SOCK_STREAM,0))<0)
	{
		perror("socket");
		return -1;
	}
	//fill struct sockaddr_in
	memcpy(&data_addr,&cmdaddr,sizeof(data_addr));
	data_addr.sin_port=htons(dataport);
	//try connecting
	if (connect(datasocket,(struct sockaddr*)&data_addr,sizeof(data_addr))<0)
	{
		perror("dataport connect");
		return -1;
	}
	//设置接收文件的上次下载位置
	sprintf(cmdbuf,"REST %d\r\n",filesize);
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	sprintf(remotename,"%s/%s",remotepath,filename);
	sprintf(cmdbuf,"RETR %s\r\n",remotename);
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	if(ret==150)
	{ 
		receiveend=0;
		//检查是否收到226,如果收到则表示数据下载完成.
		tmpbuf=strchr(recvbuffer,'\n');
		tmpbuf++;
		if(strcmp(tmpbuf,""))
		{
			val=strtok(tmpbuf," ");
			if (!strcmp(val,"226"))
				receiveend=1;
			
		}
		FtpStatus=FTP_TRANSING;
		do
		{	
			do//开始下载文件
			{
				recvlen=recv(datasocket,databuf,sizeof(databuf),0);
				if(recvlen==-1) 
				{
					perror("ftp recv data return");
					break;
				}
				else if(recvlen==0)
				{
					if(receiveend==1)//如果是最后的数据,则退出
						goto DownloadFileEnd;
					else//离开循环去检查命令端口的数据
						break;
				}
				else
				{
					fwrite(databuf,1,recvlen,fp);
					fflush(fp);
					CheckTime(1);
				}
			}while(FtpStatus==FTP_TRANSING);
			//等待下载文件结束
			if(FtpStatus==FTP_STOPPING)
				break;
			if(recv(CmdSocket,recvbuffer,sizeof(recvbuffer),0)==-1)
				perror("ftp recv cmd return");
			Dprintf("SERVER:%s",recvbuffer);
			val=strtok(recvbuffer," ");
			if (!strcmp(val,"226"))
				receiveend=1;
			if(CheckTime(0)==0)
				break;
		}while(FtpStatus==FTP_TRANSING);
		//用户终止下载
		if(FtpStatus==FTP_STOPPING)
		{
			ret=SendFtpCommand(CmdSocket,"ABOR\r\n");
		}
	}
DownloadFileEnd:
	FtpStatus=FTP_CONECTED;
	//关闭临时文件
	fclose(fp);
	shutdown(datasocket,0);
	return 0;
}
/**上传文件,暂不支持断点续传
	remotepath:远程ftp服务器目录
	localpath:本机目录
	filename:要上传的文件名.
	返回:0成功,-1失败.
*/
int FtpZhu::PutFile(const char *remotepath,const char *localpath,const char*filename)
{
	int ret,datasocket,dataport,sendlen,recvlen;
	struct sockaddr_in data_addr;
	char databuf[1024],cmdbuf[128],*val;
	char tmpbuf[1200],*tmpptr,*thisstr,*nextstr;
	char localname[128];
	FILE *fp;
	int fileoffset;
	ftpfiledis fdis;
	
	//检测ftp必须连接上服务器
	if(FtpStatus!=FTP_CONECTED)
		return -1;
	//获得数据端口
	ret=SendFtpCommand(CmdSocket,"PASV\r\n");
	if(ret!=227) return -1;
	val=strtok(recvbuffer,",");
	for(int i=0;i<4;i++)
		val=strtok(NULL,",");
	dataport=atoi(val);
	val=strtok(NULL,")");
	dataport=dataport*256+atoi(val);
	//Dprintf("data port is %d\n",dataport);
	if((datasocket=socket (AF_INET,SOCK_STREAM,0))<0)
	{
		perror("socket");
		return -1;
	}
	//fill struct sockaddr_in
	memcpy(&data_addr,&cmdaddr,sizeof(data_addr));
	data_addr.sin_port=htons(dataport);
	//try connecting
	if (connect(datasocket,(struct sockaddr*)&data_addr,sizeof(data_addr))<0)
	{
		perror("dataport connect");
		return -1;
	}
	//检查本地文件是否已经存在,如果不存在则创建
	sprintf(cmdbuf,"CWD %s\r\n",remotepath);
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	//发送LIST命令获得文件列表
	sprintf(cmdbuf,"LIST \r\n");
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	//接收文件列表
	memset(tmpbuf,0,sizeof(tmpbuf));
	fileoffset=0;//如果找不到文件,从0开始.
	do//开始下载文件
		{
			recvlen=recv(datasocket,databuf,sizeof(databuf),0);
			if(recvlen==-1)
			{
				perror("ftp recv data return");
				break;
			}
			else if(recvlen==0)
			{
				if(CheckTime(0)==0)
					break;
			}
			else
			{
				printf("%s",databuf);
				strcat(tmpbuf,databuf);
				thisstr=tmpbuf;
				tmpptr=strchr(tmpbuf,'\n');
				while(tmpptr!=NULL)
				{
					*tmpptr='\0';
					nextstr=(char*)(tmpptr+1);
					if(strcmp(thisstr,"")!=0)
					{
						sscanf(thisstr,"%s%d%s%s%d%s%d%s%s",&fdis.prior,&fdis.usern,&fdis.username,&fdis.groupname
							,&fdis.filesize,&fdis.date,&fdis.day,&fdis.time,&fdis.filename);
						if(strcmp(fdis.filename,filename)==0)
						{
							fileoffset=fdis.filesize;
							goto findoffset;
						}
					}
					thisstr=nextstr;
					tmpptr=strchr(thisstr,'\n');
				}
				//保存上次未解析完的字符串.
				strcpy(tmpbuf,thisstr);
			}
		}while(FtpStatus==FTP_TRANSING);
	
	
findoffset:	
	//等待数据传送完成.
	if(recv(CmdSocket,recvbuffer,sizeof(recvbuffer),0)==-1)
		perror("ftp recv cmd return");
	Dprintf("SERVER:%s",recvbuffer);
	//val=strtok(recvbuffer," ");
	shutdown(datasocket,1);
	close(datasocket);
	
	Dprintf("FILEOFFSET is %d\n",fileoffset);
	//再次获得数据端口
	ret=SendFtpCommand(CmdSocket,"PASV\r\n");
	if(ret!=227) return -1;
	val=strtok(recvbuffer,",");
	for(int i=0;i<4;i++)
		val=strtok(NULL,",");
	dataport=atoi(val);
	val=strtok(NULL,")");
	dataport=dataport*256+atoi(val);
	//Dprintf("data port is %d\n",dataport);
	if((datasocket=socket (AF_INET,SOCK_STREAM,0))<0)
	{
		perror("socket");
		return -1;
	}
	//fill struct sockaddr_in
	memcpy(&data_addr,&cmdaddr,sizeof(data_addr));
	data_addr.sin_port=htons(dataport);
	//try connecting
	if (connect(datasocket,(struct sockaddr*)&data_addr,sizeof(data_addr))<0)
	{
		perror("dataport connect");
		return -1;
	}
	sprintf(localname,"%s/%s",localpath,filename);
	fp=fopen(localname,"rb");
	fseek(fp,fileoffset,SEEK_SET);
	sprintf(cmdbuf,"REST %d\r\n",fileoffset);
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	sprintf(cmdbuf,"STOR %s\r\n",filename);
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	if(ret!=150) return -1;
	FtpStatus=FTP_TRANSING;
	while(!feof(fp) && FtpStatus==FTP_TRANSING)//开始上传文件
	{
		sendlen=fread(databuf,1,sizeof(databuf),fp);
		if(sendlen==-1)
		{
			perror("ftp send data return");
			break;
		}
		else
		{
			ret=send(datasocket,databuf,sendlen,0);
			if(ret==-1)
			{
				perror("Upload file failed!");
				break;
			}	
		}
	}
	shutdown(datasocket,1);
	close(datasocket);
	if(recv(CmdSocket,recvbuffer,sizeof(recvbuffer),0)==-1)
			perror("ftp recv cmd return");
	Dprintf("SERVER:%s",recvbuffer);
	val=strtok(recvbuffer," ");
	if (!strcmp(val,"226"))
		goto returnroot;
	//用户终止上传
	if(FtpStatus==FTP_STOPPING)
	{
		ret=SendFtpCommand(CmdSocket,"ABOR\r\n");
		return -1;
	}
returnroot:
	FtpStatus=FTP_CONECTED;
	//关闭数据文件
	fclose(fp);
	
	//返回根目录
	sprintf(cmdbuf,"CWD /\r\n",remotepath);
	ret=SendFtpCommand(CmdSocket,cmdbuf);
	return 0;
}
/**断开ftp连接.
*/
int FtpZhu::Disconnect()
{
	if(FtpStatus==FTP_TRANSING)
		FtpStatus=FTP_STOPPING;
	if(FtpStatus==FTP_CONECTED)
	{
		shutdown(CmdSocket,2);
		close(CmdSocket);
		FtpStatus=FTP_DISCONNECTED;
	}
	SendFtpCommand(CmdSocket,"QUIT\r\n");
	return 0;
}
/**获取当前ftp状态.
*/
int FtpZhu::GetFtpStatus()
{
	return FtpStatus;
}


#ifdef TESTZHUFTP
static FtpZhu fz;
static void SignalHandle(int signo)
{
	if(signo==SIGALRM)
	{
		Dprintf("Receive signal alarm!\n");
		fz.Disconnect();
	}
	return ;
}
//测试ftp
int main()
{
	//fz.ConnectServer("20.3.254.254","mediabus","zhu");
	fz.ConnectServer("192.168.0.159","mediabus","zhu");
	//signal(SIGALRM,SignalHandle);
	//alarm(12);
	Dprintf("Now Test Downloading Files\n");
	fz.GetFile("media",".","zhu25.mpg");
	sleep(3);
	Dprintf("Now Test Uploading Files\n");
	fz.PutFile("tmp",".","zhu25.mpg");
	sleep(1);
	fz.Disconnect();
	return 1;
}
#endif