FTPClass.cpp
上传用户:ap04031022
上传日期:2022-02-14
资源大小:1981k
文件大小:13k
源码类别:

Ftp客户端

开发平台:

Visual C++

  1. /*/////////////////////////////////////////////////////////////////////
  2. FTPclient.cpp (c) GDI 1999
  3. V1.0.0 (10/4/99)
  4. Phil Anderson. philip@gd-ind.com
  5. Simple FTP client functionality. If you have any problems with it,
  6. please tell me about them (or better still e-mail me the fixed
  7. code). Please feel free to use this code however you wish, although
  8. if you make changes please put your name in the source & comment what
  9. you did.
  10. Nothing awesome going on here at all (all sockets are used in
  11. synchronous blocking mode), but it does the following
  12. things WinInet doesn't seem to:
  13. * Supports loads of different firewalls (I think, I don't
  14.   have access to all types so they haven't all been fully
  15.   tested yet)
  16. * Allows you to execute any command on the FTP server
  17. * Adds 10K to your app install rather than 1Mb #;-)
  18. Functions return TRUE if everything went OK, FALSE if there was an,
  19. error. A message describing the outcome (normally the one returned
  20. from the server) will be in m_retmsg on return from the function.
  21. There are a few error msgs in the app's string table that you'll
  22. need to paste into your app, along with this file & FTPclient.h
  23. If you created your app without checking the "Use Windows Sockets"
  24. checkbox in AppWizard, you'll need to add the following bit of code
  25. to you app's InitInstance()
  26. if(!AfxSocketInit())
  27. {
  28. AfxMessageBox("Could not initialize Windows Sockets!");
  29. return FALSE;
  30. }
  31. To use:
  32. 1/ Create an object of CFTPclient.
  33. 2/ Use LogOnToServer() to connect to the server. Any arguments
  34. not used (e.g. if you're not using a firewall), pass an empty
  35. string or zero for numeric args. You must pass a server port
  36. number, use the FTP default of 21 if you don't know what it is.
  37. 3/ Use MoveFile() to upload/download a file, 1st arg is local file
  38. path, 2nd arg is remote file path, 3rd arg is TRUE for a PASV
  39. connection (required by some firewalls), FALSE otherwise, 4th arg
  40. is TRUE to upload, FALSE to download file. MoveFile only works in
  41. synchronous mode (ie the function will not return 'till the transfer
  42. is finished). File transfers are always of type BINARY.
  43. 4/ You can use FTPcommand() to execute FTP commands (eg
  44. FTPcommand("CWD /home/mydir") to change directory on the server),
  45. note that this function will return FALSE unless the server response
  46. is a 200 series code. This should work fine for most FTP commands, 
  47. otherwise you can use WriteStr() and ReadStr() to send commands & 
  48. interpret the response yourself. Use LogOffServer() to disconnect
  49. when done.
  50. /////////////////////////////////////////////////////////////////////*/
  51. #include "stdafx.h"
  52. #include "FTPSOCKET.h"
  53. #include "FTPClass.h"
  54. #ifdef _DEBUG
  55. #undef THIS_FILE
  56. static char THIS_FILE[]=__FILE__;
  57. #define new DEBUG_NEW
  58. #endif
  59. //////////////////////////////////////////////////////////////////////
  60. // Construction/Destruction
  61. //////////////////////////////////////////////////////////////////////
  62. //构造函数,变量初始化
  63. CFTPclient::CFTPclient()
  64. {
  65. m_pCtrlsokfile=NULL;
  66. m_pCtrlTxarch=NULL;
  67. m_pCtrlRxarch=NULL;
  68. m_Ctrlsok=NULL;
  69. }
  70. CFTPclient::~CFTPclient()
  71. {
  72. CloseControlChannel();
  73. }
  74. //////////////////////////////////////////////////////////////////////
  75. // Public Functions
  76. //////////////////////////////////////////////////////////////////////
  77. // 登录到服务器
  78. BOOL CFTPclient::LogOnToServer(CString hostname,int hostport,CString username, CString password, CString acct, CString fwhost,CString fwusername, CString fwpassword,int fwport,int logontype) {
  79. int port,logonpoint=0;
  80. const int LO=-2, ER=-1;
  81. CString buf,temp;
  82. const int NUMLOGIN=9; // 支持9种不同的登录方式
  83. int logonseq[NUMLOGIN][100] = {
  84. // 下面的数组保存了针对不同防火墙的登录序列
  85. {0,LO,3, 1,LO,6, 2,LO,ER}, // 没有防火墙
  86. {3,6,3, 4,6,ER, 5,ER,9, 0,LO,12, 1,LO,15, 2,LO,ER}, // 主机名
  87. {3,6,3, 4,6,ER, 6,LO,9, 1,LO,12, 2,LO,ER}, // USER after logon
  88. {7,3,3, 0,LO,6, 1,LO,9, 2,LO,ER}, //proxy OPEN
  89. {3,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER}, // Transparent
  90. {6,LO,3, 1,LO,6, 2,LO,ER}, // USER with no logon
  91. {8,6,3, 4,6,ER, 0,LO,9, 1,LO,12, 2,LO,ER}, //USER fireID@remotehost
  92. {9,ER,3, 1,LO,6, 2,LO,ER}, //USER remoteID@remotehost fireID
  93. {10,LO,3, 11,LO,6, 2,LO,ER} // USER remoteID@fireID@remotehost
  94. };
  95. if(logontype<0||logontype>=NUMLOGIN) return FALSE; // illegal connect code
  96. if(!logontype) {
  97. temp=hostname;
  98. port=hostport;
  99. }
  100. else {
  101. temp=fwhost;
  102. port=fwport;
  103. }
  104. if(hostport!=21) hostname.Format(hostname+":%d",hostport); // 如果端口不是默认端口21,则设定端口
  105. if(!OpenControlChannel(temp,port)) return false;
  106. if(!FTPcommand("")) return FALSE; // 获得连接服务器初始化信息
  107. // 获得登录类型
  108. while(1) {
  109. switch(logonseq[logontype][logonpoint]) {
  110. case 0:
  111. temp="USER "+username;
  112. break;
  113. case 1:
  114. temp="PASS "+password;
  115. break;
  116. case 2:
  117. temp="ACCT "+acct;
  118. break;
  119. case 3:
  120. temp="USER "+fwusername;
  121. break;
  122. case 4:
  123. temp="PASS "+fwpassword;
  124. break;
  125. case 5:
  126. temp="SITE "+hostname;
  127. break;
  128. case 6:
  129. temp="USER "+username+"@"+hostname;
  130. break;
  131. case 7:
  132. temp="OPEN "+hostname;
  133. break;
  134. case 8:
  135. temp="USER "+fwusername+"@"+hostname;
  136. break;
  137. case 9:
  138. temp="USER "+username+"@"+hostname+" "+fwusername;
  139. break;
  140. case 10:
  141. temp="USER "+username+"@"+fwusername+"@"+hostname;
  142. break;
  143. case 11:
  144. temp="PASS "+password+"@"+fwpassword;
  145. break;
  146. }
  147. // 发出命令,获得响应
  148. if(!WriteStr(temp)) return FALSE;
  149. if(!ReadStr()) return FALSE;
  150. // 只有这些响应是合法的
  151. if(m_fc!=2&&m_fc!=3) return FALSE;
  152. logonpoint=logonseq[logontype][logonpoint+m_fc-1]; //get next command from array
  153. switch(logonpoint) {
  154. case ER: // 出现错误
  155. m_retmsg.LoadString(IDS_FTPMSG1);
  156. return FALSE;
  157. case LO: // L0表示成功登录
  158. return TRUE;
  159. }
  160. }
  161. }
  162. // 退出服务器
  163. void CFTPclient::LogOffServer() {
  164. WriteStr("QUIT");
  165. CloseControlChannel();
  166. }
  167. // 发送命令到服务器
  168. BOOL CFTPclient::FTPcommand(CString command) {
  169. if(command!=""&&!WriteStr(command)) return FALSE;
  170. if((!ReadStr())||(m_fc!=2)) return FALSE;
  171. return TRUE;
  172. }
  173. // 上载或者下载文件
  174. BOOL CFTPclient::MoveFile(CString remotefile, CString localfile,BOOL pasv,BOOL get) {
  175. CString lhost,temp,rhost;
  176. UINT localsock,serversock,i,j;
  177. CFile datafile;
  178. CSocket sockSrvr;
  179. CAsyncSocket datachannel;
  180. int num,numread,numsent;
  181. const int BUFSIZE=4096;
  182. char cbuf[BUFSIZE];
  183. DWORD lpArgument=0;
  184. // 打开本地文件
  185. if(!datafile.Open(localfile,(get?CFile::modeWrite|CFile::modeCreate:CFile::modeRead))) {
  186. m_retmsg.LoadString(IDS_FTPMSG4);
  187. return FALSE;
  188. }
  189. if(!FTPcommand("TYPE I")) return FALSE; // 请求二进制传输
  190. if(pasv) { // 建立被动传输方式
  191. if(!FTPcommand("PASV")) return FALSE;
  192. // 分析出服务器传回的临时IP地址以及端口号
  193. if((i=m_retmsg.Find("("))==-1||(j=m_retmsg.Find(")"))==-1) return FALSE;
  194. temp=m_retmsg.Mid(i+1,(j-i)-1);
  195. i=temp.ReverseFind(',');
  196. serversock=atol(temp.Right(temp.GetLength()-(i+1))); //get ls byte of server socket
  197. temp=temp.Left(i);
  198. i=temp.ReverseFind(',');
  199. serversock+=256*atol(temp.Right(temp.GetLength()-(i+1))); // add ms byte to server socket
  200. rhost=temp.Left(i);
  201. while(1) { // 将逗号转化成点
  202. if((i=rhost.Find(","))==-1) break;
  203. rhost.SetAt(i,'.');
  204. }
  205. }
  206. else { // 设置主动的传输模式
  207. m_retmsg.LoadString(IDS_FTPMSG6);
  208. //获取本地的ip地址,发送到服务器
  209. if(!m_Ctrlsok->GetSockName(lhost,localsock)) return FALSE;;
  210. while(1) { // 将IP地址中的点转化成逗号
  211. if((i=lhost.Find("."))==-1) break;
  212. lhost.SetAt(i,',');
  213. }
  214. // 创建本地侦听进程
  215. if((!sockSrvr.Create(0,SOCK_STREAM,NULL))||(!sockSrvr.Listen())) return FALSE;
  216. if(!sockSrvr.GetSockName(temp,localsock)) return FALSE;// get the port that MFC chose
  217. // 将端口转化成2字节,然后加入到本地IP地址中
  218. lhost.Format(lhost+",%d,%d",localsock/256,localsock%256);
  219. if(!FTPcommand("PORT "+lhost)) return FALSE;// 发送端口到服务器
  220. }
  221. // 发送 RETR/STOR 命令到服务器
  222. if(!WriteStr((get?"RETR ":"STOR ")+remotefile)) return FALSE;
  223. if(pasv) {// 如果是PASV模式,则创建socket并初始化外部数据连接,即数据传输通道
  224. if(!datachannel.Create()) {
  225. m_retmsg.LoadString(IDS_FTPMSG6);
  226. return FALSE;
  227. }
  228. datachannel.Connect(rhost,serversock); // 试图异步连接服务器
  229. }
  230. if(!ReadStr()||m_fc!=1) return FALSE; // 获得服务器响应
  231. if(!pasv&&!sockSrvr.Accept(datachannel)) return FALSE; // 接收从服务器来的内部绑定数据
  232. // 连接成功,然后进行阻塞式数据传输
  233. if((!datachannel.AsyncSelect(0))||(!datachannel.IOCtl(FIONBIO,&lpArgument))) {
  234. m_retmsg.LoadString(IDS_FTPMSG6);
  235. return FALSE;
  236. }
  237. while(1) { // 开始传输数据
  238. TRY {
  239. if(get) {
  240. if(!(num=datachannel.Receive(cbuf,BUFSIZE,0))||num==SOCKET_ERROR) break; // (EOF||network error)
  241. else datafile.Write(cbuf,num);
  242. }
  243. else {
  244. if(!(numread=datafile.Read(cbuf,BUFSIZE))) break; //EOF
  245. if((numsent=datachannel.Send(cbuf,numread,0))==SOCKET_ERROR) break;
  246. // 如果发送出去的字节少于从文件读取的字节,则调整发送指针,以使得数据发送正确
  247. if(numread!=numsent) datafile.Seek(numsent-numread,CFile::current);
  248. }
  249. }
  250. CATCH (CException,e) {
  251. m_retmsg.LoadString(IDS_FTPMSG5);
  252. return FALSE;
  253. }
  254. END_CATCH
  255. }
  256. datachannel.Close();
  257. datafile.Close();
  258. if(!FTPcommand("")) return FALSE; // 检查从服务器发送的传输结果信息
  259. return TRUE; // 传输成功
  260. }
  261. // 通过控制通道向服务器发送命令
  262. BOOL CFTPclient::WriteStr(CString outputstring) {
  263. m_retmsg.LoadString(IDS_FTPMSG6); // pre-load "network error" msg (in case there is one) #-)
  264. TRY {
  265. m_pCtrlTxarch->WriteString(outputstring+"rn");
  266. m_pCtrlTxarch->Flush();
  267. }
  268. CATCH(CException,e) {
  269. return FALSE;
  270. }
  271. END_CATCH
  272. return TRUE;
  273. }
  274. // 获得服务器的响应
  275. BOOL CFTPclient::ReadStr() {
  276. int retcode;
  277. if(!ReadStr2()) return FALSE;
  278. if(m_retmsg.GetLength()<4||m_retmsg.GetAt(3)!='-') return TRUE;
  279. retcode=atol(m_retmsg);
  280. while(1) { //处理多行服务器响应
  281. if(m_retmsg.GetLength()>3&&(m_retmsg.GetAt(3)==' '&&atol(m_retmsg)==retcode)) return TRUE;
  282. if(!ReadStr2()) return FALSE;
  283. }
  284. }
  285. //////////////////////////////////////////////////////////////////////
  286. // Private functions
  287. //////////////////////////////////////////////////////////////////////
  288. // 从服务器控制通道获取一行响应
  289. BOOL CFTPclient::ReadStr2() {
  290. TRY {
  291. if(!m_pCtrlRxarch->ReadString(m_retmsg)) {
  292. m_retmsg.LoadString(IDS_FTPMSG6);
  293. return FALSE;
  294. }
  295. }
  296. CATCH(CException,e) {
  297. m_retmsg.LoadString(IDS_FTPMSG6);
  298. return FALSE;
  299. }
  300. END_CATCH
  301. if(m_retmsg.GetLength()>0) m_fc=m_retmsg.GetAt(0)-48; // get 1st digit of the return code (indicates primary result)
  302. return TRUE;
  303. }
  304. // 打开控制通道
  305. BOOL CFTPclient::OpenControlChannel(CString serverhost,int serverport) {
  306. m_retmsg.LoadString(IDS_FTPMSG2);
  307. if(!(m_Ctrlsok=new CSocket)) return FALSE;
  308. if(!(m_Ctrlsok->Create())) return FALSE;
  309. m_retmsg.LoadString(IDS_FTPMSG3);
  310. if(!(m_Ctrlsok->Connect(serverhost,serverport))) return FALSE;
  311. m_retmsg.LoadString(IDS_FTPMSG2);
  312. if(!(m_pCtrlsokfile=new CSocketFile(m_Ctrlsok))) return FALSE;
  313. if(!(m_pCtrlRxarch=new CArchive(m_pCtrlsokfile,CArchive::load))) return FALSE;
  314. if(!(m_pCtrlTxarch=new CArchive(m_pCtrlsokfile,CArchive::store))) return FALSE;
  315. return TRUE;
  316. }
  317. // 关闭控制通道
  318. void CFTPclient::CloseControlChannel() {
  319. if(m_pCtrlTxarch) delete m_pCtrlTxarch;
  320. m_pCtrlTxarch=NULL;
  321. if(m_pCtrlRxarch) delete m_pCtrlRxarch;
  322. m_pCtrlRxarch=NULL;
  323. if(m_pCtrlsokfile) delete m_pCtrlsokfile;
  324. m_pCtrlsokfile=NULL;
  325. if(m_Ctrlsok) delete m_Ctrlsok;
  326. m_Ctrlsok=NULL;
  327. return;
  328. }
  329. //列出文件列表
  330. BOOL CFTPclient::List()
  331. {
  332. CString lhost,temp,rhost;
  333. UINT localsock,i;
  334. CFile datafile;
  335. CSocket sockSrvr;
  336. CAsyncSocket datachannel;
  337. int num, sum;
  338. const int BUFSIZE = 4096;
  339. DWORD lpArgument=0;
  340. m_buf.RemoveAll();
  341. m_buf.SetSize(BUFSIZE);
  342. if(!FTPcommand("TYPE I")) 
  343. return FALSE; // 请求二进制模式
  344. m_retmsg.LoadString(IDS_FTPMSG6);
  345. // 获取本地IP地址
  346. if(!m_Ctrlsok->GetSockName(lhost,localsock)) 
  347. return FALSE;;
  348. while(1) {
  349. // 将点转化成逗号
  350. if((i=lhost.Find("."))==-1) break;
  351. lhost.SetAt(i,',');
  352. }
  353. if((!sockSrvr.Create(0, SOCK_STREAM, NULL))
  354. || (!sockSrvr.Listen()))
  355. return FALSE;
  356. if(!sockSrvr.GetSockName(temp,localsock))
  357. return FALSE;
  358. lhost.Format(lhost+",%d,%d", localsock / 256, localsock % 256);
  359. if(!FTPcommand("PORT "+lhost)) 
  360. return FALSE;
  361. if(!WriteStr("LIST") )
  362. return FALSE;
  363. if(!ReadStr())
  364. return FALSE; 
  365. if(!sockSrvr.Accept(datachannel)) 
  366. return FALSE; 
  367. if((!datachannel.AsyncSelect(0)) || 
  368. (!datachannel.IOCtl(FIONBIO,&lpArgument))) {
  369. m_retmsg.LoadString(IDS_FTPMSG6);
  370. return FALSE;
  371. }
  372. sum = 0;
  373. while(1) { // 获得数据
  374. TRY {
  375. if(!(num = datachannel.Receive(m_buf.GetData() + sum, BUFSIZE, 0)) 
  376. || num == SOCKET_ERROR)
  377. break;
  378. TRACE("Received :%dn", num);
  379. Sleep(0);
  380. sum += num;
  381. m_buf.SetSize(sum + BUFSIZE);
  382. }
  383. CATCH (CException,e) {
  384. m_retmsg.LoadString(IDS_FTPMSG5);
  385. return FALSE;
  386. }
  387. END_CATCH
  388. }
  389. datachannel.Close();
  390. }
  391. void CFTPclient::ProcessList()
  392. {
  393. }
  394. BOOL CFTPclient::GetLine(int& ndx)
  395. {
  396. m_strLine.Empty();
  397. int nBytes = m_buf.GetSize();
  398. BOOL bLine = FALSE;
  399. while ( bLine == FALSE && ndx < nBytes )
  400. {
  401. char ch = (char)(m_buf.GetAt( ndx ));
  402. switch( ch )
  403. {
  404. case 'n': // 行尾
  405. bLine = TRUE;
  406. break;
  407. default:   // 其他情况
  408. m_strLine += ch;
  409. break;
  410. }
  411. ++ndx;
  412. }
  413. m_strLine = m_strLine.Left(m_strLine.GetLength() - 1);
  414. return bLine;
  415. }