INTERNET编程之FTP实现断点续传

首先列出我们有用的FTP命令和说明吧:

#include "afxsock.h"
/*-----------------------------------------------------------*/
USER - 设置用户名
PASS - 发送一个用户登陆密码
CWD  - 改变工作目录
LIST - 列出远程某个目录下的所有文件和子目录
DELE - 删除远程文件
PASV - 进入数据传输(提取IP和PORT)
PORT - 打开一个端口
TYPE - 设置数据传输模式
RETR - 下载文件
STOR - 上传文件到远程主机
SIZE - 返回文件大小
APPE - 续传文件到远程主机(用于断点续传)
MKD  - 建立一个远程目录
CDUP - 设置当前工作目录的父目录为工作目录
QUIT - 终止连接
NLST - 明明远程目录列表的名字
PWD  - 打印工作目录
MDTM - 返回文件的修改时间
RMD  - 删除远程文件
RNFR - 重命名远程文件
RNTO - 设置远程文件的新名字
SITE - 指定站点命令
MODE - 设置传输模式
REIN - 重新初始化连接
STAT - 返回远程主机状态
STOU - 上传一个唯一的文件
STRU - 设置文件传输结构
SYST - 返回系统类型
NOOP - 什么也不做,可以用于测试用
HELP - 返回一些帮助信息
/*-----------------------------------------------------------*/
/*-----------------------------------------------------------*/
120 服务在几分钟内就绪.
125 数据连接已经打开,传输开始.
150 文件状态OK;将要打开数据连接.
200 命令OK.
202 命令未实现,在这个站点是多余的.
211 系统状态或系统帮助的回应.
212 目录状态.
213 文件状态.
214 帮助信息.
215 NAME系统类型.
220 服务对新用户就绪.
221 服务关闭控制连接.如果合适的话推出登录.
225 数据连接打开,没有数据在传输.
226 关闭数据连接.需要的文件操作完成。.
227 进入Passive模式(h1,h2,h3,h4,p1,p2).
230 用户登入,已处理.
250 文件操作动作OK,完成.
257 "PATHNAME"已经创建.
331 用户名OK,需要密码.
332 需要登录帐号.
350 需要进行的文件操作延迟,进一步的信息.
421 服务不可用,关闭控制连接.当服务必须关闭时,这个回复可能适用任何目录.
425 无法打开数据连接.
426 连接关闭;传输异常中止.
450 需要进行的文件操作没有被接受.文件不可用 (例如,文件忙).
451 操作中止;处理时发生本地错误.
452 操作未接受.系统存储空间不足.
500 语法错误,无法识别命令.可能包括命令太长等错误.
501 参数语法错误.
502 命令未实现.
503 命令顺序错误.
504 对于该参数命令未实现.
530 未登录.
532 存储文件需要帐号.
550 需要的操作未被接受.文件不可用 (例如,文件未找到).
551 需要的操作异常中止;页类型未知.
552 需要的操作异常中止.超过了存储分配空间 (对于当前目录或数据集).
553 需要的操作未被接受.文件名不被允许.

FTP的每一个命令都有一个返回值。是一个三位数开始的返回值。每一位返回值数字具有特别的含义。
第一位数字:
 1 一个肯定的
 2 一个永久的肯定回答
 3 一个中间层次的肯定回答,服务器正在等待进一步的信息
 4 命令未被接受,需要的操作没有出现,这种情况可能是暂时的
 5 绝对的失败
第二位数字:
 0 语法错误
 1 信息内容
 2 有关传输链路的信息
 3 验证或帐号信息
 4 未使用
 5 有关文件系统状态的信息
第三位数字:
 对返回信息的进一步的分类
/*-----------------------------------------------------------*/ 

接着,我们看如何实现断点续传和断点下载吧,这里,我们封装成一个类来测试.(我只列出如何实现断点续传和断点下载的代码哦.)

class CFtp : public CObject 
{
public:
 CFtp();
 virtual ~CFtp();

 static BOOL InitSession();
 static BOOL IsNetLink(LPSTR pstrHost );

public:
 BOOL Connect(LPCSTR pstrServer,LPCSTR pstrUser = NULL,LPCSTR pstrPassword = NULL);
 BOOL Disconnect();
 BOOL IsConnect();
 
 BOOL PutFile(LPCSTR pstrLocalFile, LPCSTR pstrRemoteFile,
  DWORD dwFlags = 0, DWORD dwContext = 1);
 BOOL GetFile(LPCSTR pstrRemoteFile, LPCSTR pstrLocalFile,
  DWORD dwFlags = 0, DWORD dwContext = 1);

 LPCSTR GetLastError();
private:
 int ParsePasv(char *buf);
 int FtpCommand(LPCTSTR pszFormat,...);
private:
 LPSTR  m_pstrServer;
 LPSTR  m_pstrError;
 
 SOCKET  m_Socket;
};

我们看看实现代码吧:

CFtp::CFtp()
{
 m_pstrServer  = new CHAR[512];
 m_pstrError   = new CHAR[512];
 memset( (void*)m_pstrServer, 0, 512 );
 memset( (void*)m_pstrError, 0, 512 );
}

CFtp::~CFtp()
{
 delete []m_pstrServer;
 delete []m_pstrError;
}

BOOL CFtp::InitSession()
{
 WORD wVersionRequested;
 WSADATA wsaData;
 int err;
 
 wVersionRequested = MAKEWORD( 2, 2 );
 err = WSAStartup( wVersionRequested, &wsaData );
 if ( err != 0 ) return FALSE;
 if ( LOBYTE( wsaData.wVersion ) != 2 ||
   HIBYTE( wsaData.wVersion ) != 2 )
 {
  WSACleanup( );
  return FALSE;
 }

 return TRUE;
}

BOOL CFtp::Connect(LPCSTR pstrServer,LPCSTR pstrUser /* = NULL */,LPCSTR pstrPassword /* = NULL */)
{
 int ret;
 hostent *pHost = NULL;        //指向服务器主机的指针
 SOCKADDR_IN addrSrv;        //服务器地址信息

 sprintf( m_pstrServer, "%s", pstrServer );     //FTP服务器

 pHost = gethostbyname( m_pstrServer );
 if ( !pHost ) return FALSE;
 addrSrv.sin_addr.S_un.S_addr = *((DWORD*)pHost->h_addr_list[0]);
 addrSrv.sin_family    = AF_INET;
 addrSrv.sin_port    = htons(21);
 memset( addrSrv.sin_zero ,0  ,sizeof(addrSrv.sin_zero) );

 m_Socket = socket( AF_INET, SOCK_STREAM, 0 ); //创建套接字(socket)
 ret   = connect( m_Socket , (SOCKADDR*)&addrSrv ,sizeof(SOCKADDR) ); 
 if( ret == SOCKET_ERROR ) return FALSE;
 
 if( !pstrUser )
  ret = FtpCommand( "user anonymous/r/n" );
 else
  ret = FtpCommand( "user %s/r/n", pstrUser);
 if( ret != 220 ) goto ERR;
 
 if( pstrPassword )
  ret = FtpCommand( "pass /r/n" );
 else
  ret = FtpCommand( "pass %s/r/n", pstrPassword );
 if( ret != 331 ) goto ERR;

 //初始化连接
 Sleep(1000);
 ret = recv( m_Socket,m_pstrError,512,0);
 if ( ret == SOCKET_ERROR ) goto ERR;

 return TRUE;
ERR:
 closesocket( m_Socket );
 return FALSE;
}

BOOL CFtp::Disconnect()
{
 int ret;

 ret = FtpCommand( "QUIT /r/n" );
 closesocket( m_Socket );
 
 return TRUE;
}

BOOL CFtp::IsNetLink( LPSTR pstrHost )
{
 hostent *pHost = NULL;        //指向服务器主机的指针
 pHost = gethostbyname( pstrHost );
 if ( !pHost ) return FALSE;

 return TRUE;
}

BOOL CFtp::IsConnect()
{
 int ret;
 
 ret = FtpCommand( "NOOP /r/n" );
 if ( ret != 200 ) return FALSE;

 return TRUE;
}

int CFtp::FtpCommand(LPCTSTR pszFormat,...)
{
 int ret;
    va_list pArg;
 
 memset( m_pstrError, 0, 512 );
    va_start(pArg, pszFormat);
    _vstprintf(m_pstrError, pszFormat, pArg);
    va_end(pArg);
 
 ret = send( m_Socket, m_pstrError, strlen(m_pstrError), 0 );
 if ( ret == SOCKET_ERROR ) return ret;
 ret = recv( m_Socket, m_pstrError, 512, 0 );
 if ( ret == SOCKET_ERROR ) return ret;

 return atol( m_pstrError );
}

int CFtp::ParsePasv( char *buf )
{
 char *p = strchr( buf, '(' );
 p++; //跳过‘(’
 char *q = strchr( buf, ')' );
 if( !p || !q ) return -1;
 *( p + ( q - p ) ) = '/0';
 int port = -1;
 p = strchr( p, ',' );
 p++;
 p = strchr( p, ',' );
 p++;
 p = strchr(p,',');
 p++;
 p = strchr( p, ',' );
 p++;
 port = atol( p ) * 256 ;
 p = strchr( p, ',' );
 p++;
 port += atol( p );
 return port;
}

BOOL CFtp::PutFile(LPCSTR pstrLocalFile, LPCSTR pstrRemoteFile, DWORD dwFlags /* = 0 */, DWORD dwContext /* = 1 */)
{
 int ret;           //返回值
 hostent *pHost = NULL;        //主机指针
 int nPos = 0;          //上传文件开始位置
 char buffer[512];         //缓存
 int  port;
 
 if( !IsNetLink( m_pstrServer ) ) return FALSE;
 SOCKET sockData = socket( AF_INET, SOCK_STREAM, 0 ); //创建套接字(socket)。(用于操作文件,即建立数据连接)

 //提取ip,port,分析 (服务器IP,端口号).
 ret = FtpCommand("PASV /r/n");
 if ( ret == SOCKET_ERROR ) goto ERR;
 port = ParsePasv( m_pstrError );
 if(  port <= 0 ) goto ERR;

 //设置传输数据方式
 ret = FtpCommand("type a/r/n");
 if ( ret == SOCKET_ERROR ) goto ERR;

 SOCKADDR_IN addrto;
 pHost = gethostbyname( m_pstrServer );
 if ( !pHost ) goto ERR;
 addrto.sin_addr.S_un.S_addr = *((DWORD*)pHost->h_addr_list[0]);
 addrto.sin_family   = AF_INET;
 addrto.sin_port    = htons(port);
 memset( addrto.sin_zero, 0, sizeof(addrto.sin_zero) );

 ret = connect( sockData, (SOCKADDR*)&addrto, sizeof(SOCKADDR) ); //连接到FTP服务器
 if( ret == SOCKET_ERROR ) goto ERR;
 
 //获取远程文件已经上传的大小
 ret = FtpCommand( "size %s/r/n", pstrRemoteFile);
 if( ret == 213 ) nPos = atol(&m_pstrError[4]);
 
 ret = FtpCommand( "appe %s/r/n", pstrRemoteFile );
 if( ret >= 400/*550*/ ) goto ERR;

 //上传文件
 FILE *f;
 int nRead , nSend;
 if( ( f = fopen( pstrLocalFile, "r" ) ) == NULL ) goto ERR;
 fseek( f, nPos, SEEK_SET );
 nRead = nSend = 0;
 do
 {
  memset( buffer, '/0', 512 );
  nRead = fread( buffer, 1, 511, f );
  if( nRead <= 0 ) break;
  nSend = send( sockData, buffer, nRead + 1, 0);
  if( nSend == SOCKET_ERROR || nSend <= 0 ) break;
 }while( nRead > 0 );
 fclose( f );

 //断开连接
 ret = FtpCommand("quit /r/n");
 closesocket(sockData);

 return TRUE;
ERR:
 closesocket(sockData);
 return FALSE;
}

BOOL CFtp::GetFile(LPCSTR pstrRemoteFile, LPCSTR pstrLocalFile, DWORD dwFlags /* = 0 */, DWORD dwContext /* = 1 */)
{
 int ret;           //返回值
 hostent *pHost = NULL;        //主机指针
 int nPos = 0;          //下载文件开始位置
 char buffer[512];         //缓存
 int port;
 
 if( !IsNetLink( m_pstrServer ) ) return FALSE;
 SOCKET sockData = socket( AF_INET, SOCK_STREAM, 0 ); //创建套接字(socket)。(用于操作文件,即建立数据连接)
 
 //获取数据传输端口
 ret = FtpCommand("PASV /r/n");
 if ( ret == SOCKET_ERROR ) goto ERR;
 port = ParsePasv( m_pstrError ) ;     //分析 (服务器IP,端口号),提取ip,port
 if( port == -1 ) goto ERR;
 
 //设置数据传输方式
 ret = FtpCommand("type i/r/n");
 if ( ret == SOCKET_ERROR ) goto ERR;
 
 SOCKADDR_IN addrto;
 pHost = gethostbyname( m_pstrServer );
 if( !pHost ) goto ERR;
 addrto.sin_addr.S_un.S_addr = *((DWORD*)pHost->h_addr_list[0]);
 addrto.sin_family   = AF_INET;
 addrto.sin_port    = htons(port);
 memset( addrto.sin_zero, 0, sizeof(addrto.sin_zero) );
 
 ret = connect( sockData, (SOCKADDR*)&addrto, sizeof(SOCKADDR) );
 if( ret == SOCKET_ERROR )  goto ERR;

 //下载文件
 FILE *f;
 if( ( f = fopen( pstrLocalFile, "ab" ) ) == NULL ) goto ERR;
 fseek( f, 0L, SEEK_END );
 nPos = ftell( f );
 //从断点处开始下载
 ret = FtpCommand( "rest %d/r/n", nPos );
 if( ret != 350 )
 {
  fclose(f); goto ERR;
 }
 ret = FtpCommand( "retr %s/r/n", pstrRemoteFile);
 if( ret != 125 && ret != 150 )
 {
  fclose(f);goto ERR;
 }
 int nRecv ;
 do
 {
  memset(buffer,0,512);
  nRecv = recv(sockData,buffer,511,0);
  fwrite(buffer,nRecv,1,f);
 }while( nRecv > 0 );
 fclose(f);
 
 //断开数据连接
 FtpCommand( "quit /r/n" );
 closesocket( sockData );

 return TRUE;
ERR:
 closesocket( sockData );
 return FALSE;
}

LPCSTR CFtp::GetLastError()
{
 return m_pstrError;
}

同志们,我这里只是教大家大致的实现方式,用的时候,一定要修改哦.呵呵.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值