SocketAPI 实现FTPServer文件下载

来源:互联网 发布:女孩晒巨臀照走红网络 编辑:程序博客网 时间:2024/06/07 03:42

//.h file

#ifndef __FTPCLIENT_H__
#define __FTPCLIENT_H__

class  FTPClient
{
public: 
 FTPClient();
 virtual ~FTPClient();

public:
 //@Function create control connection, connect to ftp server, log on to ftp server,
 //   open data connection, start thread to download data.
 //@Param
 //@Return TRUE if successful to complete operations above, else return FALSE.
 BOOL Active(char* pAddress,  // ftp server address.
    int   nPort,  // ftp server port.
    char* pUserName, // user name to log on to server.
    char* pPassword, // password to log on.
    char* pRemoteFile); // remote file name located on ftp server.


 //@Function stop thread, close data connection, log off from ftp server, close control connection.
 //@Param 
 //@Return 
 void Inactive();

 //@Function get bytes currently received from ftp server.
 //@Param
 //@Return the bytes currently received.
 LONGLONG GetBytesNumber();

public:
 //@Function virtual function that will be implemented by child class to do data process.
 //   when data is receive from ftp server, this function will be called automaticly.
 //@Param char* pBuffer: data buffer that received from ftp server.
 //@Param int nBufLen: data buffer length.
 virtual BOOL OnReceive(char* pBuffer, int nBufLen);

 //@Function virtual function that should be implemented by child class to implement error process.
 //   when error occurs while downloading from ftp server, this function is called.
 //@Param DWORD dwError: error code.
 //@Param char* strError:error description.
 virtual void OnError(DWORD dwError, char* strError);

 //@Function virtual function that should be implemented by child class to implement error
 //   log when error occurs.
 //@Param DWORD dwError: error code.
 //@Param char* strError:error description.
 virtual void OnLogError(DWORD dwError, char* strError);

 //@Function virtual function that should be implemented by child class to process some operations
 //   when data is end of stream such as pop a dialog to notify user that downloading is
 //   successfully finished or notify down stream that upstream is end of stream.
 virtual void OnEndOfStream();

protected:
 BOOL EstablishCtrlConnection(char* pAddress, int nPort);
 BOOL LogOnToServer(char* pUser, char* pPassword);
 BOOL EstablishDataConnection(char* strRemoteFile);

 void CloseDataConnection();
 void LogOffServer();
 void CloseCtrlConnection();

 DWORD FunThread();
 static DWORD WINAPI EntryThread(LPVOID lParam);

private:
 //@Function read command line until command line is read successfully, user cancel, or error occurs.
 //@Param char* pBuffer: buffer to retrieve data.
 //@Param int nLen: buffer length.
 //@Param int* nCode: pointer to retrieve command response code such as 220, 150.
 //@Param int* nLenRead: data length that is read, include '/n';
 //@Param DWORD *dwError: pointer to retrieve the error code.
 //Return  0 successful to receive one complete command line.
 //    1 user cancels this operation.
 //   -1 error occurs while receiving data from ftp server.
 int ReadCmdLine(char* pBuffer, int nLen, int* nCode, int* nLenRead, DWORD* dwError);

 //@Function read one complete cmd response command until it is successful to read, user cancel, or error occurs.
 //@Param DWORD *dwError: pointer to retrieve the error code.
 //Return  0 successful to receive one complete response.
 //    1 user cancels this operation.
 //   -1 error occurs while receiving data from ftp server.
 int ReadCmdResponse(DWORD* dwError);

 //@Function send complete data until all the data is sent, user cancels, or error occurs.
 //@Param char* pBuffer: data to send. it must end with '/r/n'.
 //@Param int nLen: data length, including '/r/n';
 //@Param DWORD* dwError: pointer to retrieve error code.
 //@Return  0 successful to send all the data to ftp server.
 //    1 user cancel this operation.
 //   -1 error occurs while sending data.
 int SendCmdLine(char* pBuffer, int nLen, DWORD* dwError);

protected:
 char  m_strAddress[MAX_PATH];  // ftp server address.
 int   m_nPort;      // ftp server port.
 char  m_strUser[MAX_PATH];   // user name to log on.
 char  m_strPassword[MAX_PATH];  // user password to log on.
 char  m_strRemoteFile[MAX_PATH];  // file located on ftp server to download.
 DWORD  m_dwError;      // error code.
 LONGLONG m_llBytes;      // bytes current received.

protected:
 SOCKET  m_hSktCtrl;      // control connection to ftp server.
 SOCKET  m_hSktSvr;      // server socket handle to wait for ftp server to connect.
 SOCKET  m_hSktData;      // data connection.

protected:
 HANDLE m_hThread;      // thread handle to receive data.
 HANDLE m_hEvent;      // event to quit from thread.
 int  m_fc;       // return code.
 DWORD m_dwID;       // instance id.
 char*   m_pBuffer;

};

#endif

//cpp file

#include "stdafx.h"
#include <winsock2.h>
#include "SocketApi.h"
#include "FTPClient.h"

DWORD gdwFTPClientID = 0;

FTPClient::FTPClient():
m_nPort(21)
,m_dwError(0)
,m_llBytes(0)
,m_hSktCtrl(INVALID_SOCKET)
,m_hSktSvr(INVALID_SOCKET)
,m_hSktData(INVALID_SOCKET)
,m_hThread(NULL)
,m_hEvent(NULL)
,m_fc(0)
,m_dwID(-1)
{
 gdwFTPClientID++;
 m_dwID = gdwFTPClientID;
 m_pBuffer = new char[1025];
}

FTPClient::~FTPClient()
{
 delete[] m_pBuffer;
}

//@Function create control connection, connect to ftp server, log on to ftp server,
//   open data connection, start thread to download data.
//@Param
//@Return TRUE if successful to complete operations above, else return FALSE.
BOOL FTPClient::Active(char* pAddress,  // ftp server address
       int   nPort,  // ftp server port
       char* pUserName, // user name to log on to server
       char* pPassword, // password to log on
       char* pRemoteFile) // remote file name located on ftp server
{
 char sMsg[1000];

 m_hEvent = CreateEvent( NULL, TRUE, FALSE, NULL);
 if( m_hEvent==NULL )
 {
  m_dwError = GetLastError();
  OnLogError(m_dwError, "Error:: FTPClient Active:Failed to create event.");
  
  return FALSE;
 }
 BOOL bRet = EstablishCtrlConnection(pAddress, nPort);
 if( !bRet )
 {
  m_dwError = GetLastError();
  sprintf(sMsg,"Error::FTPClient Active:EstablishCtrlConnection %s %d %x",pAddress,nPort,m_dwError);
  OnLogError(m_dwError, sMsg);
  CloseHandle( m_hEvent);
  m_hEvent = 0;
  return FALSE;
 }

 bRet = LogOnToServer(pUserName, pPassword);
 if( !bRet )
 {
  m_dwError = GetLastError();
  sprintf(sMsg,"Error::FTPClient Active:LogOnToServer %s %s %x",pUserName,pPassword,m_dwError);
  OnLogError(m_dwError, sMsg);

  CloseCtrlConnection();
  CloseHandle( m_hEvent);
  m_hEvent = 0;
  return FALSE;
 }

 bRet = EstablishDataConnection(pRemoteFile);
 if( !bRet )
 {
  m_dwError = GetLastError();
  sprintf(sMsg,"Error::FTPClient Active:EstablishDataConnection %s %x",pRemoteFile,m_dwError);
  OnLogError(m_dwError, sMsg);

  LogOffServer();
  CloseCtrlConnection();
  CloseHandle( m_hEvent);
  m_hEvent = 0;
  return FALSE;
 }

 DWORD dwThreadID = 0;
 m_hThread = CreateThread(NULL, 0, EntryThread, (LPVOID)this, 0, &dwThreadID);
 if( m_hThread==0 )
 {
  m_dwError = GetLastError();
  sprintf(sMsg,"Error::FTPClient Active:EntryThread %x",m_dwError);
  OnLogError(m_dwError, sMsg);

  CloseDataConnection();
  LogOffServer();
  CloseCtrlConnection();
  CloseHandle( m_hEvent);
  m_hEvent = 0;
  return FALSE;
 }

 m_dwError = 0;
 return TRUE;
}


//@Function stop thread, close data connection, log off from ftp server, close control connection.
//@Param 
//@Return 
void FTPClient::Inactive()
{
 if( m_hEvent )
 {
  SetEvent( m_hEvent );
 }
 if( m_hThread )
 {
  WaitForSingleObject( m_hThread, INFINITE);
  CloseHandle( m_hThread);
  m_hThread = 0;
 }
 CloseDataConnection();
 LogOffServer();
 CloseCtrlConnection();
 if( m_hEvent )
 {
  CloseHandle( m_hEvent);
  m_hEvent = 0;
 }
}

//@Function get bytes currently received from ftp server.
//@Param
//@Return the bytes currently received.
LONGLONG FTPClient::GetBytesNumber()
{
 return m_llBytes;
}


//@Function virtual function that will be implemented by child class to do data process.
//   when data is receive from ftp server, this function will be called automaticly.
//@Param char* pBuffer: data buffer that received from ftp server.
//@Param int nBufLen: data buffer length.
BOOL FTPClient::OnReceive(char* pBuffer, int nBufLen)
{
 return TRUE;
}

//@Function virtual function that should be implemented by child class to implement error process.
//   when error occurs while downloading from ftp server, this function is called.
//@Param DWORD dwError: error code.
//@Param char* strError:error description.
void FTPClient::OnError(DWORD dwError, char* strError)
{
}

//@Function virtual function that should be implemented by child class to implement error
//   log when error occurs.
//@Param DWORD dwError: error code.
//@Param char* strError:error description.
void FTPClient::OnLogError(DWORD dwError, char* strError)
{
}

//@Function virtual function that should be implemented by child class to process some operations
//   when data is end of stream such as pop a dialog to notify user that downloading is
//   successfully finished or notify down stream that upstream is end of stream.
void FTPClient::OnEndOfStream()
{
}


BOOL FTPClient::EstablishCtrlConnection(char* pAddress, int nPort)
{
 char sMsg[2000];

 strcpy( m_strAddress, pAddress);
 m_nPort = nPort;
 m_hSktCtrl = CSocketInterface::CreateClientSocket(&m_dwError);
 if( m_hSktCtrl==INVALID_SOCKET )
 {
  sprintf(sMsg,"Error::FTPClient EstablishCtrlConnection:Failed to create client socket. %x",m_dwError);
  OnLogError(m_dwError,sMsg);
  return FALSE;
 }
 if( !CSocketInterface::Connect(m_hSktCtrl, pAddress, nPort, &m_dwError) )
 {
  sprintf(sMsg,"Error::FTPClient EstablishCtrlConnection:Failed to connect to ftp server. %s %d %x",pAddress,nPort,m_dwError);
  OnLogError(m_dwError, sMsg);
  closesocket( m_hSktCtrl );
  m_hSktCtrl = INVALID_SOCKET;
  return FALSE;
 }
 int nRet = ReadCmdResponse(&m_dwError);
 if( nRet!=0 )
 {
  sprintf(sMsg,"Error::FTPClient EstablishCtrlConnection:Failed to ReadCmdResponse %x",m_dwError);
  OnLogError(m_dwError, sMsg);

  closesocket( m_hSktCtrl );
  m_hSktCtrl = INVALID_SOCKET;
  return FALSE;
 }

 m_dwError = 0;
 return TRUE;
}
BOOL FTPClient::LogOnToServer(char* pUser, char* pPassword)
{
 strcpy(m_strUser, pUser);
 strcpy(m_strPassword, pPassword);

 int nRet = 0;
 char strBuffer[MAX_PATH];

 //send "USER pUser /r/n"
 sprintf( strBuffer, "USER %s/r/n", pUser);
 nRet = SendCmdLine(strBuffer, strlen(strBuffer), &m_dwError);
 if( nRet!=0 )
 {
  return FALSE;
 }
 nRet = ReadCmdResponse(&m_dwError);
 if( nRet!=0 )
 {
  return FALSE;
 }
 if( m_fc!=3 )//331
 {
  return FALSE;
 }

 //send "PASS pPassword/r/n"
 sprintf( strBuffer, "PASS %s/r/n", pPassword);
 nRet = SendCmdLine(strBuffer, strlen(strBuffer), &m_dwError);
 if( nRet!=0 )
 {
  return FALSE;
 }
 nRet = ReadCmdResponse(&m_dwError);
 if( nRet!=0 )
 {
  return FALSE;
 }
 if( m_fc!=2 )//230
 {
  return FALSE;
 }
 
 m_dwError = 0;
 return TRUE;
}
BOOL FTPClient::EstablishDataConnection(char* strRemoteFile)
{
 strcpy( m_strRemoteFile, strRemoteFile);
 char strBuffer[1025];
 int nRet = 0;
 int nCode = 0;
 int nRead = 0;

 //send "TYPE I/r/n".
 strcpy( strBuffer, "TYPE I/r/n" );
 nRet = SendCmdLine( strBuffer, strlen(strBuffer), &m_dwError );
 if( nRet!=0 )
 {
  return FALSE;
 }
 nRet = ReadCmdResponse(&m_dwError);
 if( nRet!=0 )
 {
  return FALSE;
 }
 if( m_fc!=2 )//200
 {
  return FALSE;
 }

 //create server socket and start to listen.
 m_hSktSvr = CSocketInterface::CreateListenSocket(0, &m_dwError);
 if( m_hSktSvr==INVALID_SOCKET )
 {
  return FALSE;
 }
 if( !CSocketInterface::Listen( m_hSktSvr, 5, &m_dwError) )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }

 //get server ip address and ip port, then send "PORT 192,168,80,68,0,21/r/n".
 char strAddress[32];
 int  nPort = 0;
 BYTE a,b,c,d;
 if( !CSocketInterface::GetSocketName(m_hSktCtrl, strAddress, &nPort, &m_dwError) ) //m_hSktSvr
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 //if( !CSocketInterface::GetSocketName(m_hSktSvr, strAddress, &nPort, &m_dwError) ) //m_hSktSvr
 //{
 // closesocket( m_hSktSvr );
 // m_hSktSvr = INVALID_SOCKET;
 // return FALSE;
 //}
 //{
 // char sMsg[1024];
 // sprintf(sMsg, "FTPClient Ftp server will connect to %s.", strAddress);
 // OnLogError(0, sMsg);
 //}
 
 if( !CSocketInterface::IP2B4(strAddress, &a, &b, &c, &d) )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 if( !CSocketInterface::GetSocketName(m_hSktSvr, strAddress, &nPort, &m_dwError) ) //m_hSktSvr
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 sprintf(strBuffer, "PORT %d,%d,%d,%d,%d,%d/r/n", a, b, c, d, nPort/256, nPort%256 );
 nRet = SendCmdLine( strBuffer, strlen(strBuffer), &m_dwError);
 if( nRet!=0 )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 nRet = ReadCmdResponse( &m_dwError );
 if( nRet!=0 )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 if( m_fc!=2 )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }

 //send "RETR filename/r/n"
 sprintf( strBuffer, "RETR %s/r/n", strRemoteFile);
 nRet = SendCmdLine( strBuffer, strlen(strBuffer), &m_dwError );
 if( nRet!=0 )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 nRet = ReadCmdResponse(&m_dwError);
 if( nRet!=0 )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 if( m_fc!=1 )//150
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }

 //accept connection from ftp server.
 nRet = CSocketInterface::CheckRead(m_hSktSvr, 10000, &m_dwError);
 if( nRet==SOCKET_ERROR || nRet==0 )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
  return FALSE;
 }
 else
 {
  m_hSktData = CSocketInterface::AcceptSocket( m_hSktSvr, &m_dwError);
  if( m_hSktData==INVALID_SOCKET )
  {
   closesocket( m_hSktSvr );
   m_hSktSvr = INVALID_SOCKET;
   return FALSE;
  }
 }

 return TRUE;
}

void FTPClient::CloseDataConnection()
{
 if( m_hSktData!=INVALID_SOCKET )
 {
  closesocket( m_hSktData);
  m_hSktData = INVALID_SOCKET;
 }
 if( m_hSktSvr!=INVALID_SOCKET )
 {
  closesocket( m_hSktSvr );
  m_hSktSvr = INVALID_SOCKET;
 }
}
void FTPClient::LogOffServer()
{
 if( m_hSktCtrl!=INVALID_SOCKET )
 {
  SendCmdLine("QUIT/r/n", strlen("QUIT/r/n"), &m_dwError);
 }
}
void FTPClient::CloseCtrlConnection()
{
 if( m_hSktCtrl!=INVALID_SOCKET )
 {
  closesocket( m_hSktCtrl );
  m_hSktCtrl = INVALID_SOCKET;
 }
}

DWORD FTPClient::FunThread()
{
 const DWORD BUFFERSIZE = 4096*20;
 char* pBuffer = new char[BUFFERSIZE];
 int nRet = 0;
 DWORD dwError = 0;

 m_llBytes = 0;
 while( 1 )
 {
  //check if user want to cancel this operation.
  if( WaitForSingleObject(m_hEvent, 0)==WAIT_OBJECT_0 )
  {
   break;
  }

  //check if there are some data to read.
  nRet = CSocketInterface::CheckRead(m_hSktData, 500, &dwError);

  //if error occurs.
  if( nRet==SOCKET_ERROR )
  {
   char* pMsg = 0;
   BOOL bRet = CSocketInterface::GetLastErrorDes(&pMsg, dwError);
   if( bRet )
   {
    OnError(dwError, pMsg);
    CSocketInterface::ReleaseMessage(pMsg);
   }
   else
   {
    OnError(dwError, "unknown error.");
   }
   break;
  }
  //if there are some data to read,just read them.
  else if( nRet==1 )
  {
   int nRead = recv(m_hSktData, pBuffer, BUFFERSIZE, 0);
   //if end of stream, we notify this to external system.
   if( nRead==0 )
   {
    OnEndOfStream();
    break;
   }
   //if data is successful to read, update bytes downloaded.
   else if( nRead!=SOCKET_ERROR )
   {
    OnReceive(pBuffer, nRead);
    m_llBytes += nRead;
   }
   //if error to read.
   else
   {
    dwError = WSAGetLastError();
    char* pMsg;
    BOOL bRet = CSocketInterface::GetLastErrorDes(&pMsg, dwError);
    if( bRet )
    {
     OnError(dwError, pMsg);
     CSocketInterface::ReleaseMessage(pMsg);
    }
    else
    {
     OnError(dwError, "unknown error");
    }
    break;
   }
  }
  //if time out to check. just continue
  else
  {
   continue;
  }

 }
 delete[] pBuffer;

 ReadCmdResponse( &dwError );
 return 0;
}
DWORD WINAPI FTPClient::EntryThread(LPVOID lParam)
{
 FTPClient* pThis = (FTPClient *)lParam;
 DWORD dwRet = pThis->FunThread();
 return dwRet;
}
//@Function read command line until command line is read successfully, user cancel, or error occurs.
//@Param char* pBuffer: buffer to retrieve data.
//@Param int nLen: buffer length.
//@Param int* nCode: pointer to retrieve command response code such as 220, 150.
//@Param int* nLenRead: data length that is read, include '/r/n';
//@Param DWORD *dwError: pointer to retrieve the error code.
//Return  0 successful to receive one complete command line.
//    1 user cancels this operation.
//   -1 error occurs while receiving data from ftp server.
int FTPClient::ReadCmdLine(char* pBuffer, int nLen, int* nCode, int* nLenRead, DWORD* dwError)
{
 int nRetCheck = 0;
 int nRetRead = 0;
 int nPos = 0;

 *nCode = 0;
 *nLenRead  = 0;
 *dwError = 0;
 while( 1 )
 {
  if( WaitForSingleObject( m_hEvent, 0)==WAIT_OBJECT_0 )
  {
   return 1;
  }
  nRetCheck = CSocketInterface::CheckRead(m_hSktCtrl, 500, dwError);
  //if there are some data to read.
  if( nRetCheck==1 )
  {
   nRetRead = recv(m_hSktCtrl, pBuffer+nPos, 1, 0);//return value are:SOCKET_ERROR, 0, 1
   if(nRetRead!=1)
   {
    *dwError = WSAGetLastError();
    return -1;
   }
   else
   {
    nPos++;
    *nLenRead = *nLenRead + 1;
    if( *(pBuffer+nPos-1)=='/n' )
    {
     *(pBuffer+nPos) = '/0';
     *nCode = *pBuffer - 48;
     //TRACE(pBuffer);
     return 0;
    }
   }
  }
  //if timeout.
  else if( nRetCheck==0 )
  {
   continue;
  }
  //we regard it is error.
  else
  {
   return -1;
  }
 }
 return 0;
}

//@Function read one complete cmd response command until it is successful to read, user cancel, or error occurs.
//@Param DWORD *dwError: pointer to retrieve the error code.
//Return  0 successful to receive one complete response.
//    1 user cancels this operation.
//   -1 error occurs while receiving data from ftp server.
int FTPClient::ReadCmdResponse(DWORD* dwError)
{
 int nRet = 0;
 int nCode = 0;
 int nLenRead = 0;

 nRet = ReadCmdLine(m_pBuffer, 1024, &nCode, &nLenRead, dwError);
 if( nRet!=0 )
 {
  return nRet;
 }
 //if it is single reply.
 m_fc = nCode;
 if( strlen(m_pBuffer)<6 || *(m_pBuffer+3)!='-' )
 {
  return 0;
 }

 int nMsg = atoi( m_pBuffer );
 //if it is multiple replies.
 while(1)
 {
  nRet = ReadCmdLine(m_pBuffer, 1024, &nCode, &nLenRead, dwError);
  if( nRet!=0 )
  {
   return nRet;
  }
  if( strlen(m_pBuffer)>=6 && *(m_pBuffer+3)==' ' && atoi(m_pBuffer)==nMsg )
  {
   return 0;
  }
 }
 return 0;

}

//@Function send complete data until all the data is sent, user cancels, or error occurs.
//@Param char* pBuffer: data to send. it must end with '/r/n'.
//@Param int nLen: data length, including '/r/n';
//@Param DWORD* dwError: pointer to retrieve error code.
//@Return  0 successful to send all the data to ftp server.
//    1 user cancel this operation.
//   -1 error occurs while sending data.
int FTPClient::SendCmdLine(char* pBuffer, int nLen, DWORD* dwError)
{
 int nPos = 0;
 int nRet = 0;

 *dwError = 0;
 while( nPos<nLen )
 {
  if( WaitForSingleObject( m_hEvent, 0 )==WAIT_OBJECT_0 )
  {
   return 1;
  }
  nRet = send( m_hSktCtrl, pBuffer + nPos, nLen - nPos, 0);
  if( nRet==SOCKET_ERROR )
  {
   *dwError = WSAGetLastError();
   return -1;
  }
  nPos += nRet;
 }

 return 0;
}

原创粉丝点击