FileZilla客户端实现分析

转载 2012年03月27日 20:11:36
Content List:

1.1 Source File Classification
Kernel CommandQueue.cpp FileZilla.cpp FileZillaApi.cpp FtpListResult.cpp MainThread.cpp SiteManager.cpp
Definition & Configuration SFtpCommandIDs.h FzApiStructures.h structures.h
Network Substrate AsyncSocketEx.cpp AsyncGssSocketLayer.cpp AsyncProxySocketLayer.cpp AsyncSocketExLayer.cpp AsyncSslSocketLayer.cpp ControlSocket.cpp IdentServerControl.cpp IdentServerDataSocket.cpp SftpControlSocket.cpp FtpControlSocket.cpp TransferSocket.cpp
Misc AsyncRequestQueue.cpp FileChangeNotify.cpp MFC64bitFix.cpp PathFunctions.cpp version.cpp ApiLog.cpp ServerPath.cpp SFtpIpc.cpp SpeedLimit.cpp DirectoryCache.cpp Crypt.cpp
GUI Related All others .They are too many and I don't wanna list them here.


1.2 Main Framework of FileZilla Client
     FileZilla客户端的GUI核心就是CMainFrame,CMainFrame在创建的时候 (CMainFrame::OnCreate)会创建一个CFileZillaApi对象(m_pFileZillaApi)和一个 CCommandQueue对象(m_pCommandQueue),FileZilla客户端为其命令通道专门创建了一线程CMainThread, 该线程采用Command模式来实现的:GUI部分通过PostThreadMessage发送FZAPI_THREADMSG_COMMAND消息给CMainThread来完成发送FTP命令,CMainThread将所有的FTP命令封装成一个t_command结构(defined in FileZillaApi.h),通过FZAPI_THREADMSG_COMMAND线程消息来接收GUI发来的FTP命令。CFileZillaApi则在GUI和 CMainFrame中充当了一个Proxy的作用,而CCommandQueue则充当着命令缓冲和序列化的作用。在CMainThread中保存了主窗口的HWND和消息ID,在必要的时候CMainThread就可以通过窗口的消息机制来通知主窗口线程。 
     我们知道在FTP的时候都是走的双通道:一个命令通道;一个数据通道。在FileZilla中命令通道对应的就是 CFtpControlSocket,数据通道对应的就是CTransferSocket.在CMainThread初始化的时候 (CMainThread::InitInstance),就创建了CFtpControlSocket,在GUI发命令要求连接FTP服务器的时候,其 调用 Connect去连接远程FTP服务器。数据传输通道的建立则是在通过命令通道发送了LIST命令,FTP服务器返回之后,才创建了 CTransferSocket对象,并将其作为一个Server,调用Listen等待FTP服务器的连接。数据通道主要是接收LIST命令返回的服务 器文件树和DOWNLOAD命令传来的下载文件的数据(见CTransferSocket::OnReceive)。 
     FileZilla客户端提供了KeepAlive机制,在CMainThread中设置了一个Timer,如果配置了OPTION_KEEPALIVE选项,系统就会在空闲时向服务器 随机的发送PWD,REST 0 ,TYPE A ,TYPE I 命令,以避免FTP服务器因为客户端连上来后长时间没有动作而自动关闭与客户端的连接。 

1.3 Network Implementation Analysis

     FileZilla的网络部分的结构图如下:

其中CAsyncSocketEx是对MFC的CAsyncSocket的改进,据说比CAysncSocket性能上要高一点。在CAsyncSocketEx中添加了对CAsyncSocketLayer的支持,各个SocketLayer 链接成一个双向的链表(见CAsyncSocketExLayer::m_pNextLayer和CAsyncSocketExLayer::m_pPrevLayer),在CAsyncSocketEx中记录了这个链表的头尾指针。
     对 FileZilla来说网络就是其核心,而FillaZilla的网络部分主要就是CAsyncSocketEx和 CAsyncSocketExLayer了,下面我们来分析一下CAsyncSocketEx和CAsyncSocketExLayer的工作机制。 
     首先讲讲CAsyncSocketEx,其实在MFC里面有一个对基于HWND的异步事件的Winsock管理模型的封装类 CAsyncSocket,但作者Tim Kosse这里又重新了一个CAsyncSocketEx类提供了跟MFC的CAsyncSocket相同的接口方法和功能,在eMule开源项目中我发 现其网络部分也是用的这个FileZilla中的CAsyncSocketEx。那么CAsyncSocketEx比CAsyncSocket好,好在哪 里呢?Tim Kosse在解释为什么CAsyncSocketEx比MFC的CAsyncSocket快时说:"每 个使用CAsyncSocket的线程都会创建一个window,CAsyncSocket就是使用这个窗口的句柄来调用WSAAsyncSelect 的,直到这里CAsyncSocketEx和CAsyncSocket都是一样的,但是对一个线程中的所有Socket,CAsyncSocket仅仅使 用一个消息WM_SOCKET_NOTIFY,其wParam是 套接字的句柄,而window通过一个内部建立好的map,根据这个套接字的句柄来获取CAsyncSocket的Instance.而 CAsyncSocketEx就不同了,它使用了从WM_USER到0xBFFF的消息来为每个Socket提供一个通知消息,这样window在接受到 一个消息后,将 index of message - WM_USER作为内部数组的Index来获取CAsyncSocketEx的Instance.所以CAsyncSocketEx就要比 CAsyncSocket快一些,特别是在管理大量的Socket的时候,性能的提升还是比较明显的。".另外,CAsyncSocketEx还提供了a flexible layer system和套接字的状态机实现。 

     接下来我们来分析一下CAsyncSocketEx的Layer系统是如何工作的.
    
从上图我们可以看到,CAsyncSocketEx将 Receive,Send,Close,Connect,Create,Listen,Accept,ShutDown,GetPeerName,GetSockName 这些调用按照左边的顺序一层一层的往下抛,一直到最后一个Layer才真正调用相应的API函数。同时,Helper窗口函数在收到通过 WSAASyncSelect机制发过来的事件时,首先将其发给最下面一层处理,然后 按照上图右边的顺序,从下往上通过OnReceive,OnClose,OnAccept,OnConnect,OnSend 一层一层的调用。
     同时,CAysncSocketLayer还使用WM_USER消息将其内部错误反馈给CAsyncSocketEx(通过 TriggerEvent,PostMessage到HelperWindow);在CAysncSocketLayer和CAsyncSocketEx 之间还有一个Callback机制,CAsyncSocketExLayer::DoLayerCallback把消息封装成 t_callbackMsg,调用CAsyncSocketEx::AddCallbackNotification添加到CAsyncSocketEx 的内部队列m_pendingCallbacks中等待处理。 
     我感觉对CAsyncSocketLayer的机制还不是很清晰,下面再进一步分析一个Layer的实现吧.在FileZilla中已 经实现了三个Layer了:ProxyLayer,sFtp Layer和SSL Layer.下面找个最简单的分析一下啦,就看Proxy Layer吧:) CAsyncProxySocketLayer主要是提供了对SOCKSv4,SOCKSv5和HTTP1.1 代理的支持。通过其源代码的分析,其也主要是在Connect和GetHostByName这两个部分做了一些处理,使得在连接应用服务器之前先连接上代 理服务器,并对客户端进行认证,在代理服务器上建立circuits .其他的就没什么了。在 CAsyncSocketLayer的实现中,可以看到其主要还是利用SendNext,ReceiveNext,XXXXNext方法来处理数据,而不 是在末尾调用CAsyncSocketLayer基类的同样方法。在发现错误的时候通过调用TriggerEvent和DoLayerCallback来 向上层报告相关的网络错误。 

1.4 Get Nuggets from Misc
多语言支持
     FileZilla客户端的多语言支持采取的是资源DLL的形式,每种Language都被编译成一个独立 的DLL,这个DLL中包含了FileZilla的所有Dialog,Menu,String Table,Toolbar,Version,Accelarator资源。本来不想去研究具体实现的,但我开了个Win32 DLL工程把rc文件加进去才发现我生成的文件大小是108K,而FileZilla发布的FzResChs.dll才76K ,而且我里面就是一个dummy DllMain函数,为什么会有这么大的文件大小差距呢?纯资源DLL难道不是这样做的么?我用eXeScope发现FzResChs.dll里面竟然没有.text和.rdata等段,呵呵:),原来它没有代码的(这也正是为什么叫做纯资源DLL吧?),于是在Project Setting的Link Table中加入/NOENTRY /nodefaultlib,从工程中删除stdafx.h stdafx.cpp *.cpp,重新编译。Okey,这下我们的DLL也76K啦^_^.
     资源DLL是编译好了,下一步就是如何使用这个DLL啦。其实蛮简单的,在GUI主线程初始化的时候(CWinApp::InitInstance),加载这个资源DLL,然后调用AfxSetResourceHandle(hResourceDll),将本线程的资源句柄设置为这个资源DLL就可以了。对于非MFC程序,就不能用AfxSetResourceHandle,可以用 FindResource,LoadResource,LockResource来获取DLL模块中的资源。但这个感觉比较麻烦哦。不知到Windows有没有提供跟AfxSetResourceHandle功能一样的API可以使用呢?我想应该是没有这样的函数了:( 

带消息处理的线程类封装
     以前比较喜欢MFC的CWinApp,因为它就是一个GUI线程类,支持线程消息。在FileZilla中也有类似这样的线程,可是它不是从CWinApp派生的,而是自己写的一个。来看一下它是如何实现线程消息的吧!
     首先定义一个static的线程函数ThreadProc,并将其传给CreateThread.ThreadProc函数里通过 GetMessage/TranslateMessage/DispatchMessage的一个消息Pump来获取线程的消息。在获取到消息后再调用 OnThreadMessage做消息的分发处理。发消息直接通过PostThreadMessage API调用来完成啦。到现在,我想我们都必须再次看看GetMessage的MSDN文档了,MSDN中指出以hWnd=NULL为参数调用GetMessage,会获取到线程消息队列中所有窗口和通过PostThreadMessage API调用发送过来的消息。而每个Win32线程都有自己的消息队列的。 

Option配置选项和属性页的封装
     在FileZilla项目中有众多的Option和属性页,下面我们就来分析一下FileZilla是如何组织这些属性页的.下图是FileZilla的选项页:

FileZilla把所要的属性页放在Option.h中,然后将Option.h加到stdafx.h中使其全局化。其做了一个属性页的GUI框架,所 有的属性页从CSAPrefsSubDlg派生,而主框架从 CSAPrefsDialog派生。这个很自然(我写的UpdaterManager也是这样做的哦^_^),FileZilla将所有的配置不再写到系 统注册表中(现在都提倡绿色软件啦),而是保存到一个XML格式配置文件FileZilla.xml中,这个文件就放在安装目录下。
     现在看看SetOption和GetOption是如何实现的,FileZilla 把所有的Option的值分为int型和string型两类,并用t_Option结构记录每个Option在XML配置文件中的节点名和Option的 值类型(0为字符串;1为整型),然后用一个static t_Option options[OPTIONS_NUM]的数组来做nOptionID到Option对应的t_Option结构的映射。SetOption和GetOption通过这个nOptionID来指定相应的Option.并且每次都需要调用 Init函数装载配置文件FileZilla.xml来实现Option的Set/Get操作。为了缓解每次文件存取的延迟,COption机制还使用了Cache的思想(见static t_OptionsCache m_OptionsCache[OPTIONS_NUM]),在系统启动是读取配置,以后每次对 配置的更改都会写到配置文件中并且Cache起来。这样下次Get的时候就可以直接取了不用在去加载配置文件了。 

Base64 Encoding
     Base64编码主要是将任意长度的sequences of octets编码成not be humanly readable。根据RFC2045介 绍,编码后的数据将比编码前最多大33%左右。Base64 Encoding将3个字节的数据(3*8=24bits)编码成4个Base64 Encoding 字母表中的字母(4*6=24bits),Base64 Alphabet就是字母(大小写一共52个),数字(10个)和+ /一共64个字符组成,所以叫做Base64编码。每个Base64 Alphabet中的字母用一个6bits来表示。Base64编码将编码后的字符按行来组织,每行不得多于76个字符。另外对于编码最后少于3个字节 的,采取补零和用"="来标示。具体可以参考RFC2045. 

UTF-8
     最早只有Ascii和EBCDIC编码,后来为了支持软件的国际化,使用了UTF-8 Encoding。为支持多国的字符编码,ISO/IEC 10646定义了两套 编码方案:UCS-4 和 UCS-2。UCS-4使用四个字节来标示一个字符;UCS-2使用两个字节来标示一个字符。Unicode标准的Version 2只和ISO/IEC 10646的UCS-2编码方案兼容。UTF-8是UCS Transformation Format 8的缩写(Abbreviation),它只是一个传输格式,并不是字符集哦。UTF- 8的基本思想是用1到6个字节 来表示一个UCS字符,根据UCS字符的编码设置这1-6个字节的Most significant bits为相应的1/0串。根据UTF-8编码后的Sequence的第一个字节的最前面的1的数量就可以知道是用多少个字节来表示这个UCS字符的。比 如对于UTF-8编码110xxxxx 10xxxxxx ,我们就知道其使用2个字节来表示这个UCS字符。 

1.5 Explore FileZilla Client GUI 
 
上图给出了FillZilla客户端GUI界面的框架:最上面是一个Rebar(m_pWndReBar),它由一个 Toolbar(m_pWndToolBar)和一个DialogBar(m_pWndDlgBar)组成。 最下面的是一个Status Bar (m_wndStatusBar)。中间是一个Splitter窗口(m_wndVertSplitter),这个SplitterWnd是三行一列的, 第一行是一个CStatusView,第三行是一个CQueueView;第二行又是一个一行两列的 SplitterWnd,左边是LocalFile View,右边是RemoteFile View;并且这两个File View都是一个两行一列的SplitterWnd. 复杂吧? ^_^ 

1.7 Knowledge Repository
获取一个Socket的接收缓冲的Pending Data的数据量大小:
     ioctlsocket(hSocket,FIONREAD,&dwBytes); 

WSAAsyncGetHostByName:
     这是gethostbyname的异步版本。在获取完成后会以指定的消息发送到指定的窗口。 

WSACancelAsyncRequest:
     取消未完成的异步Winsock操作,这些操作主要就是WSAAsyncGetXByY 型的异步调用。 

FindFirstChangeNotification:
     使用FindFirstChangeNotification/FindNextChangeNotification/FindCloseChangeNotification,可以实现对 一个目录及其子目录下的所有文件和文件夹的监控,如果其文件名,文件大小,文件的最后写时间等属性改变了我们可以立刻得到通知。 

1.8 Summarize
     Okey, 总结一下FileZilla客户端,我认为其结构还是组织得比较合理的:GUI和网络部分的接口使用Proxy模式,采用独立的FTP命令处理线程和命令 模式来发送FTP命令;另外,采用资源DLL的方式来支持多语言;采用独立的XML配置文件和static函数来支持对系统的众多配置;同时,使用了总多 的Cache和Queue技术来支持 顺序操作和提高访问效率。技术上来说,其比较核心的就是Tim Kosse的那套支持代理和SSL的CAsyncSocketEx和CAsyncSocketLayer机制了。但我感觉好像整个系统里面就好像只有两个 线程:一个GUI主线程和一个CMainFrame。感觉是不是太少了,将数据通道放在单独的线程中处理可能对提高系统的效率有一定的作用。同 时,socket的数据缓冲管理,在FileZilla中没有做特殊的处理,没有使用缓冲池和Lookaside List等类似的技术。当然,FileZilla只是一个客户端,我们不能 对其要求太高^_^。

Filezilla使用教程

本文转载自点击打开链接 FileZilla是一款免费开源的FTP客户端软件,虽然它是免费软件,可性能却一点也不含糊,比起那些共享软件来有过之而无不及,具备大多数的FTP软件功能。其可控性、有条理的界面...
  • Z_ying
  • Z_ying
  • 2015年07月15日 11:32
  • 746

wxWidgets初学者导引(4)——wxWidgets学习资料及利用方法指导

4 wxWidgets学习资料及利用方法指导  初学者常苦于找不到参考资料。实际上,是找不到,不是没有。真正有用的资料,常常也就在手边,只是不知道。有能力熟练地使用一切能用得着的资料,这是水平提高的指...

英语学习也可以“做中学”

英语学习在我国各教育阶段均受到学校非同一般的重视,然而学生普遍的表现,却也让我体会到英语学习确实是一个学习的重灾区,是不讲学习方法最集中、最典型的科目,是学习目标异化最严重,应试教育毒害最深的领域。看...

FTP客户端软件FileZilla使用教程

描述: FileZilla是一个免费开源的FTP客户端软件,分为客户端版本和服务器版本,具备所有的FTP软件功能.可控性、有条理的界面和管理多站点的简化方式使得Filezilla客户端版成为一个方便高...

filezilla快速搭建FTP服务器和客户端使用教程

有时候公司很多资料需要共享,对于共享的资料而言,公司员工通常需要做一些比如“上传、下载”的操作,那么搭建一个FTP服务器环境就相当有必要。   带图原文:http://download.csdn....

如何配置filezilla服务端和客户端

如何配置filezilla服务端和客户端 百度‘filezilla server’下载最新版。注意点:下载的版本如果是32位的适用xp和win2003,百度首页的是适用于win7或更高的win系统。...

FileZilla客户端使用遇到的问题

filezilla应对windows访问vsftpd中文部分乱码问题。以及filezilla 550 failed to change directory 问题。...

FTP客户端软件FileZilla使用教程(转自:http://hi.baidu.com/%B8%DF%C7%E5%D6%AE%D3%D1/blog/item/36d6c8b6dc0589fc30a)

描述: FileZilla是一个免费开源的FTP客户端软件,分为客户端版本和服务器版本,具备所有的FTP软件功能.可控性、有条理的界面和管理多站点的简化方式使得Filezilla客户端版成为一个方便高...
  • lslxdx
  • lslxdx
  • 2011年09月29日 14:10
  • 2046

Filezilla(ftp客户端)

  • 2013年06月01日 10:09
  • 4.22MB
  • 下载

FileZilla-3.7.0.1 FTP客户端

  • 2016年05月31日 21:17
  • 5.63MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:FileZilla客户端实现分析
举报原因:
原因补充:

(最多只允许输入30个字)