﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-eamon100-文章分类-Filezilla Server</title><link>http://www.cppblog.com/eamon100/category/17732.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 19 Jan 2012 18:15:04 GMT</lastBuildDate><pubDate>Thu, 19 Jan 2012 18:15:04 GMT</pubDate><ttl>60</ttl><item><title>四、网络相关的代码</title><link>http://www.cppblog.com/eamon100/articles/161405.html</link><dc:creator>eamon</dc:creator><author>eamon</author><pubDate>Sun, 04 Dec 2011 02:49:00 GMT</pubDate><guid>http://www.cppblog.com/eamon100/articles/161405.html</guid><wfw:comment>http://www.cppblog.com/eamon100/comments/161405.html</wfw:comment><comments>http://www.cppblog.com/eamon100/articles/161405.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eamon100/comments/commentRss/161405.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eamon100/services/trackbacks/161405.html</trackback:ping><description><![CDATA[本章分析与网络相关的代码<img src ="http://www.cppblog.com/eamon100/aggbug/161405.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eamon100/" target="_blank">eamon</a> 2011-12-04 10:49 <a href="http://www.cppblog.com/eamon100/articles/161405.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>三、流程控制</title><link>http://www.cppblog.com/eamon100/articles/161404.html</link><dc:creator>eamon</dc:creator><author>eamon</author><pubDate>Sun, 04 Dec 2011 02:46:00 GMT</pubDate><guid>http://www.cppblog.com/eamon100/articles/161404.html</guid><wfw:comment>http://www.cppblog.com/eamon100/comments/161404.html</wfw:comment><comments>http://www.cppblog.com/eamon100/articles/161404.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eamon100/comments/commentRss/161404.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eamon100/services/trackbacks/161404.html</trackback:ping><description><![CDATA[Filezilla Server采用标准的Windows系统服务模式，入口代码放在Service.cpp 主要用户处理用户对该系统服务的控制，如启动、停止、重启等。由于采用的不是桌面应用程序方式，给软件调试带来一定的困难。<br /><br />Service.cpp:系统服务程序入口 
<hr />

<p>WinMain是整个服务的入口函数，在WinMain里依次完成了下面几项任务：</p>
<p>1、参数解析，根据参数类型设置相应的操作如安装、卸载、启动、停止。SetServiceName设置服务名，SetServiceDisplayName设置在服务管理器中显示的名字。系统还提供了一种Compact模式，通过CompatMain执行系统入口，该函数仅提供启动、停止、设置管理员端口和重新加载配置四种功能。 <br />2、初始化某些数据。通过LoadServiceName加载服务名和显示名到全局变量中。如果入口参数设置为设置管理端口，则通过SetAdminPort 设置管理员端口，&nbsp;如果为重载配置，使用&nbsp;ReloadConfig()函数完成重载配置，该函数调用了SendReloadConfig函数，枚举所有顶层窗口，并向本程序的窗口发送WM_FILEZILLA_RELOADCONFIG消息，然后由SCM（服务控制管理器）向本系统服务发送128号消息。[此处似乎重复处理了，服务在处理128号消息时又会调用SendReloadConfig函数，可能是为了处理服务没有启动就重载配置的情况]<br />3、由SCM（服务控制管理器）启动服务，入口为ServiceMain函数；如果服务不存在，进入步骤4 <br />4、根据参数设置服务，例如安装、启动、卸载等。 <br />服务启动时，通过StartServiceCtrlDispatcher 将服务的控制权转移到ServiceMain，它注册ServiceCtrlHandler来处理服务收到的消息，在回调函数ServiceCtrlHandler中自定义了128号控制码，用于向窗口"FileZilla Server Helper Window"发送重新读取配置的消息WM_FILEZILLA_RELOADCONFIG。serviceMain注册Handler成功之后，创建了&nbsp;killServiceEvent 事件。然后启动工作线程ServiceExecutionThread，并将服务的状态调整为SERVICE_RUNNING，然后等待killServiceEvent 事件和工作线程结束允许，并将状态调整为SERVICE_STOPPED，程序结束。<br />工作线程ServiceExecutionThread首先初始化Winsock网络库，如果初始化不成功就设置killServiceEvent 事件，将状态调整为SERVICE_STOPPED,并返回结束该线程和整个程序的执行。然后，创建了CServer对象，创建消息循环将实际流程交由CServer，之后进入线程的消息循环并等待killServiceEvent信号以退出线程终止服务。<br /><br />服务状态的更新都是通过UpdateServiceStatus函数实现，该函数设置SERVICE_STATUS变量的值，调用SetServiceStatus设置服务的状态，设置不成功就调用KillService向主窗口发送WM_CLOSE关闭消息。主窗口结束运行后，工作线程的消息循环结束，并设置killServiceEvent 结束程序，服务。&nbsp;<br /><br />Server.cpp:真正的流程处理类 
<hr />

<p>&nbsp;构造函数创建了一个CAdminInterface类的指针，并将自身作为参数传递给这个指针的构造函数，并将所在线程的优先级设置为THREAD_PRIORITY_ABOVE_NORMAL。</p>
<p><font face="Verdana" color="#000000"><span style="background-color: yellow">Create</span>()创建一个类名和窗口民均为"FileZilla Server Helper Window"的窗口，这是整个程序的主窗口。并将this指针附到窗口上，方便处理发送给该窗口的消息。然后将窗口句柄赋给全局变量hMainWnd。随后初始化 option、log 和 banmanager三个指针。根据配置文件创建一定数量的处理线程，每个线程都有一个WM_FILEZILLA_SERVERMSG+ID的通知ID，GetNextThreadNotificationID获得下个一个可用的ID号。用于区分不同线程发送的消息。包括两个定时器，m_nTimerID和m_nBanTimerID，在OnTimer函数中处理。再往下调用了CreateListenSocket函数，这个函数根据Options类中获取的port、bindip、enablessl等参数创建监听ftp客户端连接的CListenSocket对象指针，并保存到m_ListenSocketList中。最后调用CreateAdminListenSocket函数创建监听admin客户端的socket，并存入m_AdminListenSocketList中。</font><font face="Verdana" color="#000000"><br /><span style="background-color: yellow">OnClose</span>(); 首先关闭所有m_ListenSocketList中的监听端口，如果m_ThreadArray和m_ClosedThreads均为空，即所有处理线程都已关闭，则销毁主窗口，退出。否则，给两个链表的线程发送FTM_GOOFFLINE的消息，让其下线。<br /><span style="background-color: yellow">GetHwnd</span>：返回创建窗口的句柄。<br />void <span style="background-color: yellow">OnTimer</span>(UINT nIDEvent);处理两个定时器消息，1234（m_nTimerID）和1235（m_nBanTimerID）分别于10秒和60秒触发，m_nTimerID用于检测管理端连接的超时，日志文件是否超过限制，m_nBanTimerID用于检测禁止用户的解禁。<br />unsigned int <span style="background-color: yellow">GetNextThreadNotificationID</span>();查找下一个可用的通知ID号，m_ThreadNotificationIDs数组包含所有已经创建的线程的指针。首先查找有没有可用的，没有则在数组最后添加一个空的对象，并返回它的索引号。<br />void <span style="background-color: yellow">FreeThreadNotificationID</span>(CServerThread *pThread);删除某个线程的编号记录，将对于的指针设置为0。这里并没有用delete删除线程的指针，因为在线程退出时它已经自动删除了。<br /></font><font face="Verdana" color="#000000">void <span style="background-color: yellow">SendState</span>();将服务器的状态m_nServerState通过m_pAdminInterface发送给管理端窗口。<br /><span style="background-color: yellow">ShowStatus</span>，它的任务是将信息发送给admin窗口和记录到log中，他是两个重载的函数，其中一个包括消息的时间。<br />下面是几个比较重要的处理函数，包括消息处理、创建监听器、处理管理端发回的命令。<br /><span style="background-color: yellow">WindowProc </span>处理发送给主窗口的消息，WM_CLOSE、WM_TIMER消息均调用相应成员函数处理，WM_FILEZILLA_RELOADCONFIG自定义消息调用COptions和CPermissions的ReloadConfig函数重新加载配置文件的参数设置。WM_DESTROY退出主处理流程，首先给所有线程发送WM_QUIT消息，并等待线程退出，然后，关闭并删除m_AdminListenSocketList中的所有监听器，删除COption指针，关闭m_nTimerID定时器，并向系统发送PostQuitMessage退出主窗口的消息循环（此处没有关闭m_nBanTimerID定时器，对m_AdminListenSocketList也没有处理，不知道为什么？？）。线程发送过来的消息交给OnServerMessage函数处理。<br />LRESULT <span style="background-color: yellow">OnServerMessage</span>(CServerThread *pThread, WPARAM wParam, LPARAM lParam);处理线程发送给主窗口的消息。pThread为发送消息线程的指针。这个函数比较复杂，慢慢分析。<br />BOOL <span style="background-color: yellow">ToggleActive</span>(int nServerState);变换服务器的状态。在defs.h 中定义了服务器的6个状态。包括在线、离线、锁定等。如果nServerState为GOOFFLINE_NOW 则关闭所有的ListenSocket，清空Listensocket列表，向ClosedThread发送FTM_GOOFFLINE消息，向m_ThreadArray中的每个ServerThread发送FTM_GOOFFLINE消息，并将其加入到ClosedThread列表。清空m_ThreadArray列表，并从服务器状态中去除ONLINE位。如果ClosedThread不为空则在服务器状态中添加GOOFFLINE_NOW 位。如果nServerState为GOOFFLINE_LOGOUT则关闭所有的ListenSocket，清空Listensocket列表，向ClosedThread发送FTM_GOOFFLINE消息，向m_ThreadArray中的每个ServerThread发送FTM_GOOFFLINE消息，并将其加入到ClosedThread列表。清空m_ThreadArray列表，并从服务器状态中去除ONLINE位。如果ClosedThread不为空则在服务器状态中添加GOOFFLINE_LOGOUT 位。如果nServerState为GOOFFLINE_WAITTRANSFER则关闭所有的ListenSocket，清空Listensocket列表，向ClosedThread发送FTM_GOOFFLINE消息，向m_ThreadArray中的每个ServerThread发送FTM_GOOFFLINE消息，并将其加入到ClosedThread列表。清空m_ThreadArray列表，并从服务器状态中去除ONLINE位。如果ClosedThread不为空则在服务器状态中添加GOOFFLINE_WAITTRANSFER位。如果nServerState为ONLINE先看ListenSocketList是否为空，为空则创建监听Socket，然后创建ServerThread，然后设置ListenSocketList中的m_bLocked属性位。最后设置ClosedThread为&#8220;等待直到退出&#8221;状态，并更改服务器的状态变量，调用SendState函数发送服务器状态。<br />BOOL <span style="background-color: yellow">ProcessCommand</span>(CAdminSocket *pAdminSocket, int nID, unsigned char *pData, int nDataLength);AdminInterface调用该函数，处理AdminSocket接收到的命令，以后慢慢分析。<br />BOOL <span style="background-color: yellow">CreateListenSocket</span>();创建FTP监听Socket，首先确定端口，如果 配置文件没有设置则采用SSL端口，如果都没有设置则返回错误信息。此处可以设置多个端口。对每个端口（包括SSL端口）和设置的IP绑定都要创建一个CListenSocket，继承自CAsyncSocketEx类，参数包括CServer指针和SSL标志，然后开始监听，对监听或创建失败的端口和IP通过ShowStatus显示给管理客户端并记录到日志。<br />BOOL <span style="background-color: yellow">CreateAdminListenSocket</span>();创建管理监听Socket，涉及到的配置文件选项包括：管理员Ip绑定，端口CAdminListenSocket 继承自CAsyncSocketEx类，包含一个CAdminInterface的指针。首先如果绑定IP为*，则监听所有的本机的网络地址，否则只监听127.0.0.1的本地管理Socket，端口为配置文件中设置的端口。如果创建失败，则创建一个127.0.0.1的Socket，端口由系统分配。创建成功则将端口保存到变量nAdminPort中，通过对话框提示用户。创建成功以后开始监听，并将指针添加到列表中。最后，对IPBinding中的每一个IP创建Socket并监听，添加到列表中，无法创建的地址则通过对话框显示。<br /><br />int DoCreateAdminListenSocket(UINT port, LPCTSTR addr, int family);<br /><br /></font></p>
<p><font face="Verdana" color="#000000">下面看一下几个线程类：CServerThread</font></p>
<p><font face="Verdana" color="#000000"></font>&nbsp;</p>
<p><br /></p><img src ="http://www.cppblog.com/eamon100/aggbug/161404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eamon100/" target="_blank">eamon</a> 2011-12-04 10:46 <a href="http://www.cppblog.com/eamon100/articles/161404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>二、源代码分析</title><link>http://www.cppblog.com/eamon100/articles/156655.html</link><dc:creator>eamon</dc:creator><author>eamon</author><pubDate>Wed, 05 Oct 2011 03:19:00 GMT</pubDate><guid>http://www.cppblog.com/eamon100/articles/156655.html</guid><wfw:comment>http://www.cppblog.com/eamon100/comments/156655.html</wfw:comment><comments>http://www.cppblog.com/eamon100/articles/156655.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/eamon100/comments/commentRss/156655.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/eamon100/services/trackbacks/156655.html</trackback:ping><description><![CDATA[文件目录结构，首先预览一下源码目录文件夹下的大致文件布局。<br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/eamon100/17733/o_AllFiles.JPG" /><br /><br />源代码目录包含7个子目录：<br /><br />
<table style="width: 639px; height: 289px" border="1">
<tbody>
<tr>
<td style="text-align: center"><span style="font-weight: bold">子目录</span><br /></td>
<td style="text-align: center"><span style="font-weight: bold">功能</span><br /></td></tr>
<tr>
<td>hash_algorithms</td>
<td>MD5，SHA等哈希算法的实现</td></tr>
<tr>
<td>includes</td>
<td>只有一个子目录openssl，<a href="http://baike.baidu.com/view/2438396.htm" target="_blank"><font color="#136ec2">安全套接层协议</font></a>，提供安全数据传输</td></tr>
<tr>
<td style="vertical-align: top">install</td>
<td style="vertical-align: top">安装脚本和资源</td></tr>
<tr>
<td style="vertical-align: top">interface</td>
<td style="height: 40px; vertical-align: top">服务器管理客户端</td></tr>
<tr>
<td style="vertical-align: top">misc</td>
<td style="vertical-align: top">混杂类，比较重要的如md5,StdString等</td></tr>
<tr>
<td style="vertical-align: top">res</td>
<td style="width: 484px; height: 30px; vertical-align: top">程序编译资源，目前只有一个icon，程序的图标</td></tr>
<tr>
<td style="vertical-align: top">tinyxml</td>
<td style="vertical-align: top">著名的一款基于DOM模型小巧开源的xml解析器</td></tr></tbody></table><br />当前source目录下源码按实现功能大致又分为以下几种类型：<br /><br />
<table style="width: 878px; height: 259px" border="1">
<tbody>
<tr>
<td style="text-align: center; font-weight: bold">功能分类<br /></td>
<td style="text-align: center; font-weight: bold">包括的文件<br /></td></tr>
<tr>
<td>网络相关</td>
<td>全体文件名含socket的，Server.*</td></tr>
<tr>
<td style="vertical-align: top">多线程相关</td>
<td style="width: 723px; height: 123px; vertical-align: top">文件名包含Thread的文件</td></tr>
<tr>
<td style="vertical-align: top">辅助<br /></td>
<td style="vertical-align: top">
<p>version.*，MFC64bitFix.*，conversion.*，config.h，service.cpp</p>
<p>等除去网络和线程的文件</p></td></tr></tbody></table><br />文件目录结构分析完了。下面从VC生成的C++代码中最常出现的两个文件stdafx.h和stdafx.cpp开始，我们首先要弄清楚它们到底包含了哪些头文件，定义了哪些宏，什么了哪些函数以及结构体。在头文件stdafx.h中包含了config.h,misc\stdstring.h,MFC64bitFix.h,conversion.h,AsyncSocketEx.h等<br /><br />config.h：<font color="#000000" size="3" face="Arial">检查是否定义了两个常量。</font> <br />
<hr style="width: 100%; height: 2px" />
强制使用unicode编译和检测是否安装了最新SDK。<br /><br />misc\stdstring.h：定义了<font color="#000000" size="3" face="Arial">CStdStr模板类，对标准模板库的basic_string&lt;&gt;</font> 进行封装，提供了许多新功能<br />
<hr style="width: 100%; height: 2px" />
非常棒的封装类，作者：Joe O'Leary，主页：<a href="http://home.earthlink.net/~jmoleary">http://home.earthlink.net/~jmoleary</a><br />参考codeproject上的页面获得最新版本：<a href="http://www.codeproject.com/KB/string/stdstring.aspx">http://www.codeproject.com/KB/string/stdstring.aspx</a><br />提供方便使用的字符串类，有MFC类CString的所有函数，CStdStringA和CStdStringW对于多字节和Unicode字符串，这个类比较大，需要慢慢分析<br /><br />MFC64bitFix.h：定义了几个获得文件状态的函数（大小，文件属性，文件指针位置，64位，支持大型文件） 
<hr style="width: 100%; height: 2px" />
定义了文件状态的结构体CFileStatus64，包含文件大小，创建时间，最后修改和访问时间，文件属性（CFile::Attribute枚举值的OR）。定义了三个函数：<br />GetLength64：获得文件的大小，通过FindFirstFile获得文件属性，然后通过属性的移位运算获得文件大小（64位）<br />GetStatus64：获得文件的属性，通过FindFirstFile获得文件属性，复制到结构体CFileStatus64中。<br />GetPosition64：获得文件指针的当前位置，使用SetFilePointer函数移动文件指针,对于大文件可以使用SetFilePointerEx，比较方便，参考MSDN。<br /><br />Conversion.h：用于ANSI和UTF8字符的互相转换 
<hr style="width: 100%; height: 2px" />
ConvFromNetwork: 用于网络字节的转换，从网络字节转换为Unicode字节，使用MultiByteToWideChar函数，先尝试CP_UTF8（UTF-8 code page），若失败则用CP_ACP（ANSI code page）。<br />ConvToNetworkW/ConvToNetworkA：用于网络字节的转换，WideCharToMultiByte<br />ConvToLocal：用于本地，从wchar_t转换位char，WideCharToMultiByte<br />ConvFromLocal：用于本地，从char转换位wchar_t，MultiByteToWideChar<br /><br />AsyncSocketEx.h：异步Scoket类的定义，它是大多数Socket类的基类 
<hr style="width: 100%; height: 2px" />
网络相关的头文件，定义了CAsyncSocketEx类，以后再详细分析。<br /><br />第55行遇到了条件宏#ifdef MMGR,编译条件中有定义，包含misc/mmgr.h文件。mmgr是用于管理和跟踪内存的代码，之后会重点详细分析。<br /><br />至此，stdafx.h中头文件包含全部结束，下面就是宏定义了。<br /><br />先看一下各消息的值范围和作用，见下图：<br /><br /><img border="0" alt="" src="http://www.cppblog.com/images/cppblog_com/eamon100/17733/o_message.JPG" width="706" height="234" /><br /><br />注册了WM_FILEZILLA_THREADMSG消息用来线程间通信，使用RegisterWindowMessage在系统中定义新的窗口消息，并确保在系统中是唯一的。返回值为0xC000&nbsp;至 0xFFFF。通常用于两个合作进程之间的通信。仅在多个进程需要处理相同消息时使用这个函数。<br />定义了WM_FILEZILLA_SERVERMSG用于进程间通信，(WM_APP + 1); 即FileZilla server.exe和FileZilla Server Interface.exe。<br /><br />下面一些宏如下：<br />FSM_STATUSMESSAGE：在管理窗口或log中显示并记录状态信息<br />FSM_CONNECTIONDATA：和连接相关的信息，如新用户连接，登录，退出等<br />FSM_THREADCANQUIT：退出线程<br />FSM_SEND：发送数据时用于管理窗口统计发送字节数<br />FSM_RECV：接受数据时用于管理窗口统计接收字节数<br />其余的就不多写了，宏名比较直观的显示出意思。后面是一些结构体的定义：<br />t_controlmessage<br />t_statusmsg<br />t_connectiondata<br />
<p>t_connectiondata_add<br />t_connectiondata_changeuser<br />t_connectiondata_transferinfo<br />t_connectiondata_transferoffsets<br />t_connop<br /><br /></p>
<p>extern HWND hMainWnd：定义了全局的CServer的窗口类句柄。</p>
<p>最后定义了一个CCriticalSectionWrapper类和两个帮助检测临界区死锁的函数，尤其是前者，DEBUG版本时错误的使用将导致当前线程挂起。<br /><br />其他一些辅助头文件和类定义。<br /><br />Version.h：用于获取程序的版本信息 
<hr style="width: 100%; height: 2px" />
定义了GetVersionString函数，返回程序版本信息的字符串，GetModuleFileName（）获取可执行文件的全路径， GetFileVersionInfoSize（）获得版本信息的大小。GetFileVersionInfo（）获得文件的版本信息，VerQueryValue查询某个选项的值，有三种路径形式：\ , \StringFileInfo\lang-codepage\string-name 和\VarFileInfo\Translation。<br /><br />Defs.h：这个类定义了服务器的状态，如在线、离线、锁住等 
<hr style="width: 100%; height: 2px" />
定义了一些服务器状态的宏。<br /><br />SpeedLimit.h：速度限制（包括时间段限制） 
<hr style="width: 100%; height: 2px" />
这里针对UI性比较强，定义了CSpeedLimit类，对应配置文件中，&lt;SpeedLimits&gt;下的一条Rule规则。Load函数加载一个&lt;Rule&gt;对应的选项，并把它保存在成员变量中。Save保存一个限制规则到TiXmlElement的对象中。GetRequiredBufferLen返回一个规则需要的字节数。IsItActive（）判断谋一时间是否有起效的规则。FillBuffer这个函数将所有限制条件格式化成一个13个字节的字符串，ParseBuffer则是解析这个字符串，采用这个类，可以轻松实现强大的自定义限速功能。<br />SPEEDLIMITSLIST为规则数组，一个用户可以对应很多条速度限制规则<br /><br />Options.h, OptionTypes.h：系统配置选项设置 
<hr style="width: 100%; height: 2px" />
OptionTypes.h中定义了一个结构t_Option，包括选项名，类型和是否只能本地连接修改。0表示字符串类型，1为数字类型。数组m_Optinons，保存所有配置项信息，如是否使用SSL，同时在线最大用户数量，上传下载限速等等，所有这些大部分都被使用在Option那个对话框UI上。数组中与Admin相关的选项以及Server name 和 server display name为TRUE。定义了SERVER_VERSION和PROTOCOL_VERSION常量，用于AdminSocket。<br /><br /><span style="background-color: yellow">Options类</span>就是操作配置文件的实体类（使用tinyXML），服务器的配置文件存储在exe同级目录下，叫FileZilla Srver.xml。Options的主要操作是针对内存中的配置，只有与默认值不同的项才会存入配置文件中。<br />Options类中定义了五个类变量：<br />m_Sync：用于多线程修改类变量的同步。<br />m_InstanceList：类实例的链表。<br />m_sOptionsCache：选项值在内存中的缓存。<br />m_bInitialized：是否已经初始化。<br />m_sSpeedLimits：速度限制设置数组。<br />定义了四个类函数：<br />IsNumeric：判断一个字符串是否是由0~9的数字组成的无符号数字。<br />GetXML：打开XML配置文件，返回XML文档的根节点。整个程序中对配置文件的访问都要通过这个函数，可以保证只有一个实例在访问XML配置文件，保持同步。<br />FreeXML：释放打开的XML文档，根据需要保存修改后的配置到配置文件中。<br />UpdateInstances：通过给每个实例的Helper Window发送异步消息，更新所有实例的值，将选项缓存更新为False，速度限制数组更新为类变量对应的值。<br />成员变量只有2个：<br />m_SpeedLimits：速度限制设置数组。<br />m_OptionsCache：缓存的各个选项值。<br />成员函数：<br />在构造函数中初始化了选项缓存，和Helper Window指针，将自身加入到类的实例链表中。初始化速度限制数组为类中对应的值。<br />析构函数从类的实例链表中删除自己，并释放Helper Window对象。<br />ReadSpeedLimits：读取配置文件中的速度限制配置。读取Download和Upload的选项内容，对每个规则生成SpeedLimit的一个对象，并放入到类变量m_sSpeedLimits数组想应的vector中。（此处没有使用同步变量，应该在同步环境中调用它）<br />SaveSpeedLimits：将类变量m_sSpeedLimits数组中的速度限制规则写入配置文件的XML树中。（此处没有使用同步变量，应该在同步环境中调用它）<br />SaveOptions：将选项保存到XML配置文件中。（为什么使用的是m_OptionsCache？可能是该变量与类变量的值相同？）<br />ReloadConfig：重新加载配置，如果配置文件不存在则创建一个新的空配置文件，若存在则加载它，通过SetOption设置到变量中。<br />Init: 初始化配置，如果已经初始化（m_bInitialized==true），则直接返回。如果配置文件不存在则创建一个新的空配置文件，若存在则加载它。如果选项值在类中没有缓存，通过SetOption设置到类变量和对象变量中。<br />GetOptionVal\GetOption:获得一个配置的值，如果对象的已经缓存则直接返回该值，否则看类变量中是否缓存，如没有则返回该选项的默认值，同时在该对象中缓存该值。<br />SetOption：将一个选项值设置到类变量和对象变量中，并根据参数保存到配置文件。如果类没有初始化则调用Init函数先初始化。<br />GetCurrentSpeedLimit：获得当前时间的速度限制的值，-1表示没有速度限制。参数为限制类型，0为下载，1为上传。速度限制有三种类型：0无限制，1限制总速度，2基于规则的限制。<br />GetAsCommand:将选项值转换为一个字节串供网络传输，前两个字节为选项的个数，高字节在前，低字节在后。每个选项的第一个字节表示选项类型，字符串和数字。对于字符串选项，每个选项的前三个字节表示该选项值的长度，管理员的密码选项做了特殊处理，不通过网络传输；对于数字选项，用8个字节表示数字的值。最后保存速度限制规则，前2个字节为规则的条数。<br />ParseOptionsCommand:解析从网络传输的选项字符串，通过SetOption保存到变量中,通过SaveOptions（）保存到配置文件中。基本上是GetAsCommand的逆向操作。<br /><br />Options还有一个隐藏的friend窗体类 COptionsHelperWindow，定义在cpp文件中，这个类用于通过用post WM_USER给窗体消息这种异步的方式去更新option实例，而不是options类自身。<br />有了Options类和OptionTypes.h中定义的配置类型，就可以通过诸如 m_pOptions-&gt;GetOptionVal(OPTION_ENABLELOGGING)这样的方法方便的获取到配置。<br /><br />OptionLimits.h：选项值设置的范围限制 
<hr style="width: 100%; height: 2px" />
定义了两个宏：OPTION_AUTOBAN_ATTEMPTS_MIN和OPTION_AUTOBAN_ATTEMPTS_MAX，自动禁用功能启用的次数限制。<br /><br />FileLogger.h：系统的日志功能 
<hr style="width: 100%; height: 2px" />
这个类中包含Options类的一个对象指针，用来读取日志文件的相关配置。CheckLogFile函数检查是否启用了日志记录功能，创建日志目录和日志文件，目前有两种日志方式：所有日志共有同一个文件和每天一个日志文件，nLogType对应为0和1。根据程序选项，删除老日志文件以及大小超过限制的文件。Log函数记录系统日志，首先ConvToNetwork将Unicode字符串转换为char*，后调用WriteFile写日志，日志文件为UTF-8格式文本文件。<br /><br />iputils.h：判断IP合法性以及是否处于某个过滤范围 
<hr style="width: 100%; height: 2px" />
与IP地址相关的辅助函数，支持IPV6，共包含8个函数。这些函数较以前的版本有较大的修改，取消了boost库中的regex类的使用，转而使用cidr表示法，支持IPV6。<br />IPv6地址（<a href="http://baike.baidu.com/view/5228.htm">http://baike.baidu.com/view/5228.htm</a>）为128位长，但通常写作8组，每组为四个十六进制数的形式。2001:0db8:85a3:08d3:1319:8a2e:0370:7344是一个合法的IPv6地址。如果四个数字都是零，可以被省略。<br />例如：2001:0db8:85a3:0000:1319:8a2e:0370:7344等价于2001:0db8:85a3::1319:8a2e:0370:7344。<br />IPv6 地址有两种短格式： 
<p>&#8226;省略前导零 <br />通过省略前导零指定 IPv6 地址。例如，IPv6 地址 1050:0000:0000:0000:0005:0600:300c:326b 可写为 1050:0:0:0:5:600:300c:326b。 <br />&#8226;双冒号 <br />通过使用双冒号（::）代替一系列零来指定 IPv6 地址。例如，IPv6 地址 ff06:0:0:0:0:0:0:c3 可写为 ff06::c3。一个 IP 地址中只可使用一次双冒号。<br /></p><br />
<p>bool IsLocalhost(const CStdString&amp; ip)：判断ip是否为本地地址，IPV4以"127."开头，IPV6短地址为"::1"。<br />CStdString GetIPV6LongForm(CStdString short_address)：获得短地址的IPV6长形式，每个IPV6长地址对应39个字符。首先去除地址的"[]"中括号，改为小写字母形式。然后查找"::"，分别对前后两部分处理。<br />bool IsIpAddress(const CStdString&amp; address)：检查是否是一个IP地址，可以是IPV6地址或IPV4地址，（不包括CIDR格式的后面部分）</p>
<p>CStdString GetIPV6ShortForm(const CStdString&amp; ip)：获得最短的IPV6形式，此处先将ip转换为标准的IPV6长形式，去除前导0，合并一系列的0。<br />bool IsValidAddressFilter(CStdString&amp; filter)：判断是否是合法的地址过滤字符串。filter可以是单个地址或采用cidr表示的地址段（ipv4或v6），掩码位为1~127.前部为一个IPV4地址或IPV6地址。若为IPV6地址则转换为标准的短格式。<br />bool IsRoutableAddress(const CStdString&amp; address)：判断一个IP地址是否是可路由的IP。对IPV4,127，10. ，192.168,169.254，以及172.（16~32）部分均为不可路由地址。对IPV6有4类：（<a href="http://zh.wikipedia.org/wiki/IPv6">http://zh.wikipedia.org/wiki/IPv6</a>）<br /><font face="Courier New">未指定地址<br />::/128－ 所有比特皆为零的地址称作未指定地址。这个地址不可指定给某个网络接口，并且只有在主机尚未知道其来源IP时，才会用于软件中。路由器不可转送包含未指定地址的分组。<br />Link local地址<br />::1/128－ 是一种单播绕回地址。如果一个应用程序将分组送到此地址，IPv6堆栈会转送这些分组绕回到同样的虚拟接口（相当于IPv4中的127.0.0.1）。<br />fe80::/10－ 这些link-local地址指明，这些地址只在区域连接中是合法的，这有点类似于IPv4中的169.254.0.0/16。<br />唯一区域位域<br />fc00::/7－唯一区域地址（ULA，unique local address）只可在一群网站中绕送。这定义在RFC 4193中，是用来取代site-local位域。这地址包含一个40比特的伪随机数，以减少当网站合并或分组误传到网络时碰撞的风险。这些地址除了只能用于区域外，还具备全局性的范畴，这点违反了唯一区域位域所取代的site-local地址的定义。<br />IPv4转译地址<br />::ffff:x.x.x.x/96－ 用于IPv4映射地址。（参见以下的Transition mechanisms）。<br />2001::/32－ 用于Teredo tunneling。<br />2002::/16－ 用于6to4。<br /></font>bool MatchesFilter(CStdString filter, CStdString ip)：判断ip是否与filter过滤器匹配。filter的IPV6为短格式。对单个IP地址过滤器比较简单，只需要比较两个字符串是否相同，如果ip是V6版本则要先转换为短格式。对地址范围，V6版本将两个地址均转换为长格式再比较，对于网络段长度不是4的倍数还需要使用掩码比较多余的位。V4版本则先讲地址通过inet_addr和ntohl函数转换为长整数，再通过掩码比较。<br />bool ParseIPFilter(CStdString in, std::list&lt;CStdString&gt;* output = 0)：讲过滤器字符串解析为一个过滤规则链表。首先将输入串的分隔符转换为单个空格，再最后输入串最后再添加一个空格。循环处理每个规则块。规则块可以是*和任何有效的地址过滤器（符合IsValidAddressFilter要求）。<br /><br />autobanmanager.h：阻止用户继续登录的方法类 </p>
<hr style="width: 100%; height: 2px" />
AutoBan这个设置项是一个非常浪费资源的，因为它对每一个失败的ip都要记录查询内存中的两个map<br />定义了CAutoBanManager类，包含一个COptions指针，根据系统设置对用户进行限制，m_banMap用于记录已禁止的用户，及禁止时间。m_attemptMap则用于记录用户已经尝试的次数。m_sync是一个线程同步变量。m_refCount为该类实例的个数，当一个对象被构造时，该值加一，析构时减一，最后一个对象析构时清空两个map。<br />IsBanned 查询一个IP是否被禁用了。<br />RegisterAttempt 记录一个IP一次新的尝试，如果超过最大次数则禁止该用户，并在m_banMap添加该ＩＰ，并从m_attemptMap中删除，目前禁止类型只有０，为１的代码还是空的，不执行禁止操作。<br />PurgeOutdated　根据设置的自动禁止时间，查看是否超过时常的，就从禁止列表和尝试列表中删除。<br /><br /><br />Accounts.h：账户类 
<hr style="width: 100%; height: 2px" />
Accounts.h中声明了3个类，t_directory，t_group，还有继承于t_group的t_user。sltype用于区别上传和下载，0表示下载。<br />t_directory 对应目录的选项，仅仅含有一些权限声明，相当于一个struct，被t_group和t_user使用。包含字符串dir，表示一个目录或文件，一个别名链表aliases，文件权限，目录权限和一个是否自动创建的变量，在构造函数中的默认值为false。<br />对应于&lt;Permissions&gt;的一条Permission 规则，用户和组均有多条这种规则。<br />剩余两个类主要做的事是对配置的读取分析，所有的数据都是基于字符串的。<br />t_group类对应组设置，每个组包括一个组名（group），目录的一个数组（permissions），<br />nBypassUserLimit，绕过服务器的用户限制<br />nUserLimit,：最大允许连接数量<br />nIpLimit： 每个Ip最大连接数量<br />forceSsl：用户登录时，强制使用SSL连接<br />&nbsp;int nSpeedLimitType[2]; 速度限制类型，包括无限制，默认，固定速度，使用规则等。<br />&nbsp;int nSpeedLimit[2];固定速度时的速度值<br />&nbsp;SPEEDLIMITSLIST SpeedLimits[2];<br />&nbsp;int nBypassServerSpeedLimit[2]; 是否绕过限制<br />nEnabled：是否允许组内用于访问服务器。IP过滤规则，速度限制规则（包括上传和下载）等，该类还有一个指向自身的指针，用于链接归属的组（用于用户）。<br />成员函数：<br />AccessAllowed：判断一个IP是否在IP过滤规则中，允许规则具有优先性，对于用户，如果自身规则不包含该IP还要查看归属组的IP过滤规则。<br />BypassServerSpeedLimit：返回是否绕过服务器的速度限制，1为是，0为否，其他值则看父节点的值。<br />GetCurrentSpeedLimit：根据速度限制类型nSpeedLimitType的值返回速度限制的值。0表示默认值，父节点的继承值，父节点不存在则没有限制，返回0。1表示无限制。2表示固定速度值，返回nSpeedLimit的值。3表示采用速度限制规则。<br />IsEnabled，ForceSsl，GetIpLimit，GetUserLimit，BypassUserLimit返回相应的属性值，或父节点的值<br />GetRequiredStringBufferLen：返回将一个字符串转换为网络传输的串需要的缓冲区的长度，比需要的长度加2个字节（在字符串的前面表示该字符串的字符个数）。<br />GetRequiredBufferLen：计算将一个实例通过网络传输需要的缓冲区长度。初始为9个字节，加上组名，加4个字节后开始计算IP规则的字节数，然后加2个字节开始计算目录的字节数。对于每个目录，最开始为2个字节，目录名的字节数，加两个字节开始目录别名的字节数。后是速度限制字节数，6个字节为基本限制，4个字节为规则的数量，然后是每个规则需要的字节数，最后加上备注的字节数和SSL设置的一个字节。<br />FillString：将一个字符串（Unicode）拷贝到缓冲区（需要字节转换）。<br />FillBuffer：将实例的设置转换到字节数组P中，首先是组名，然后是nIpLimit，nUserLimit各4个字节，nBypassUserLimit和nEnabled通过移位合用1个字节。然后是IP规则（包括允许和禁止规则），首先是规则的数量，2个字节，然后是每条规则复制。2个字节，permissions数组的大小，然后对该数组的每个元素，首先填充目录名，2个字节表示别名的数量，然后是填充每个别名。最后把目录的权限组合为2个字节。然后是速度限制，对上传和下载分别计算，nSpeedLimitType和nBypassServerSpeedLimit合用1个字节，速度限制值nSpeedLimit使用2个字节，后2个字节为规则的数量，然后填充每个规则。最后填充备注和SSL设置1个字节。<br />ParseString：解析并转换一个网络字符串，开始两个字节为字符串的长度。<br />ParseBuffer：解析网络传输过来的设置字符串，基本上是FillBuffer的逆操作。<br />t_user类继承自t_group类，多了一个用户名和密码。GetRequiredBufferLen，FillBuffer和ParseBuffer只针对用户名和密码修改了相应的函数，比较直观。<br /><br />permission.h：对用户、群组访问资源进行鉴权，对应整个程序对Group和User的权限设置 
<hr style="width: 100%; height: 2px" />
权限配置信息记录在FileZilla Server.xml中。<br />服务器对每一个group和user都有权限限制，group权限优先于user权限，在CheckFilePermissions 函数中可以看出。<br />定义了CUser类继承自t_user类，表示一个用户。<br />homedir表示用户的主目录<br />结构体t_alias定义了别名，包括targetFolder目标文件夹和name别名名称。<br />两个multimap 映射对象：aliasMap，virtualAliasNames<br />一个 map映射对象： virtualAliases。<br />其中aliasMap用于物理地址的别名映射，其他两个用于虚拟地址别名映射，即别名以&#8216;/&#8217;开头。<br />DoReplacements函数将参数路径中的:u替换为用户名，:g替换为组名（如果指定了组）。<br />PrepareAliasMap函数准备好别名映射。首先将aliasMap和virtualAliases清空。然后循环处理用户每个目录对应的每个别名设置。每个目录的别名分为两种情况：1是以'/'开头的别名，2是不以'/'开头 但是包含'\\'的别名。<br />对第一种情况：以最后一个'/'将别名分为两个部分，若前部分为空，这前部分设置为'/'。virtualAliasNames的第一个值为前部分，第二个值为后一部分。virtualAliases插入第一个值为别名+'/'，第二个值为正在处理的目录。这是一个Map，后面会覆盖前面的值。<br />对第二种情况：以最后一个'\\'将别名分为两个部分，后半部分不能为空。其值为t_alias结构的name值，targetFolder对应正在处理的目录。aliasMap的第一个值为前半部分，第二个值为这个t_alias结构。<br />GetAliasTarget函数，返回别名对应的目标文件夹。如果别名找不到则返回空串。首先看aliasMap，如果path和name与<br />aliasMap的值相同则返回目标文件夹。如果找不到则看virtualAliases，如果virtualPath的值与virtualAliases的值相同，则返回目标文件夹。<br /><br />CPermissions比较复杂，与前面的COption类的定义方式有些类似，均定义了一个辅助Helper类。<br />CPermissions还有一个隐藏的friend窗体类 CPermissionsHelperWindow，定义在cpp文件中，这个类用于通过用post WM_USER给窗体消息这种异步的方式去更新permissions实例的m_GroupsList和m_UsersList为类对应的值，而不是permissions类自身。<br /><br />CPermissions类的分析：<br />类变量：<br />m_sync&nbsp; 同步变量。<br />m_sUsersList&nbsp; 用户数组<br />m_sGroupsList 组数组<br />m_sInstanceList&nbsp; 所有实例的链表<br />类函数：<br />AddLongListingEntry 将文件的属性信息、目录信息添加到pResult链表中，每个项以'\r\n'结束。I4I64D表示14个字符的64位整数。<br />AddShortListingEntry只将文件名、目录名添加到pResult链表中，每个项以'\r\n'结束。<br />AddFactsListingEntry根据enabledFacts将文件或文件夹的特定项添加到pResult链表中，每个项以'\r\n'结束。enabledFacts[0]为类型，2为修改时间，1为文件大小，3为权限设置<br />GetHomeDir 获得用户的Home目录，如果physicalPath为true则返回主目录对应的本地文件系统的目录。<br />CheckDirectoryPermissions<br />CheckFilePermissions 检查文件或目录对用户的OP操作是否有效（具有相应的权限），有效则返回0。<br />GetUser 查找当前系统中是否存在名为username的用户，有则将用户信息复制到userdata中，并返回true。<br />CheckUserLogin 检查用户名，密码是否匹配。noPasswordCheck为0时进行密码比对，否则仅检查用户名是否存在。<br />GetAsCommand 将所有用户的信息作为命令字符串返回给管理客户端。将m_sGroupList和m_sUserList的信息填充到pBuffer中，nBufferLength为填充的字节数。<br />ParseUsersCommand 为GetAsCommand 的逆向操作，将命令字符串还原为所有账户的信息。更新所有实例的链表，并更新类的链表和配置文件XML。<br />AutoCreateDirs自动创建username对应的用户设置了AutoCreate属性的文件夹的目录结构（该路径下的所有文件夹），以及所在群组的具有该属性的文件夹目录结构。<br />ReloadConfig 重新从配置文件中加载配置，先将用户和组列表清空，通过ReadSetting读取配置文件到该对象以及类属性。最后通过UpdateInstances更新所有实例的属性。<br />GetFact 获得特定目录、文件的Facts信息。首先获得对应的本地文件系统目录，如果是文件，则先获得它上一级目录的本地目录，再加上文件名。enabledFacts[0]为类型，2为修改时间，1为文件大小，3为权限设置<br /><br />DestroyDirlisting 删除pListing中的所有项。<br />成员变量：<br />m_UsersList：&nbsp; 用户数组<br />m_GroupsList：Group数组<br />m_pPermissionsHelperWindow：CPermissionsHelperWindow的指针。<br />成员函数：<br />protected成员函数。<br />Init()函数在构造函数中被调用。初始化系统设置的值。首先创建一个&nbsp;CPermissionsHelperWindow 实例并将this指数传递给它。如果是第一次调用该函数则调用ReadSettings函数初始化类变量，否则，用类变量初始化成员变量m_GroupsList和m_UsersList。最后将自身添加到实例链表中。<br />ReadSettings()函数，打开XML配置文件用到了COptions::GetXML()方法。读取Groups下每个Group的各个选项，ReadIpFilter（）读取每个IpFilter的设置，ReadPermissions（）读取Permissions下每个Permission的设置。如果没有设置Home目录，则将第一个目录设为Home目录。ReadSpeedLimits（）读取该组内的每个速度限制规则。然后对Users做相似的操作，用户中多了一个Pass的选项，为用户设置的密码。如果提供的密码不是32位的MD5 hash值，则通过MD5类转换为hash值，并将该节点的值设置为MD5变换后的值。ReadPermissions后调用CUser类的PrepareAliasMap，初始化别名的映射。设置Home目录。最后将读取的值设置的类变量中。<br />ReadIpFilter<br />ReadPermissions<br />ReadSpeedLimits 这三个函数及相应的Save函数均读取配置文件中相应的设置，更新到user或group中。<br />UpdateInstances 更新所有实例的grouplist和userlist为类的值。<br />SetKey 设置XML树中的某个元素的属性和对应的值。<br />GetRealDirectory将服务器的绝对地址转换为本地文件系统的绝对地址。首先将服务器的绝对地址按照/字符分段。获得用户的本地文件系统的Home目录。如果目录不在Home目录的文件树中，则通过别名查找是否在别名设定的文件夹中（包括用户和组设置的别名）。找到后分析文件夹的权限设置，并返回该权限设置值。<br />WildcardMatch 判断String是否符合pattern模式，pattern可以包含*通配符。<br />CanonifyServerDir 规范化目录字符串，如果newDir为空则返回currentDir。将所有\\转换为/,然后将连续的两个//变为一个/。如果newDir为/，则返回/。如果newDir为相对路径（不以/开头），则将CurrentDir作为基目录。去除路径中的 ...，区别对待包含点的文件和全为点的目录。检查目录中是否有Windows系统保留的文件名（目录名），有则返回空值。再检查是否是保留文件名后加点的形式（如COM1.sd）,是则返回空值。最后返回合并的文件目录。<br /><br /><br />public 成员函数（接口）<br />ChangeCurrentDir 更改用户的当前路径（服务器路径），检查是否具有目录读写和列目录的权限。<br />GetDirectoryListing 获得一个目录的列表，最后一个参数为格式化字符串的函数指针，该函数支持*通配符。获得显示目录的本地文件夹，如果是一个文件或本地目录不存在，则将需要显示的目录dirToDisplay中的反斜杠变为斜杠，并将连续的两个斜杠合并为一个。变换后，如果目录以斜杠结束则返回错误代码，如果文件夹不存在，且目录最后一个部分包含*，则显示去除最后一部分后的文件夹的内容，然后获得系统当前时区的设置，使显示目录以/结尾，并转换为网络传输格式。然后，循环该用户的别名映射aliasMap，如果存在该别名，且该别名的name与最后一部分相匹配则调用addfunc函数添加该行，最后处理目录下的文件和文件夹，通过addFunc添加到列表中。<br />ConvertFilename 当useUTF8为true时将文件名转为网络格式，否则转换为多字节格式。<br /><br /><br /><br />Platform.h： 
<hr style="width: 100%; height: 2px" />
针对amd64等的未对齐内存访问的宏定义，对X86结构计算机无影响。 定义了三个宏将一个地址转换为16位、32位、64位地址。<br />
<p>&nbsp;</p>
<p>&nbsp;</p><img src ="http://www.cppblog.com/eamon100/aggbug/156655.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eamon100/" target="_blank">eamon</a> 2011-10-05 11:19 <a href="http://www.cppblog.com/eamon100/articles/156655.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一、相关文件下载</title><link>http://www.cppblog.com/eamon100/articles/155695.html</link><dc:creator>eamon</dc:creator><author>eamon</author><pubDate>Tue, 13 Sep 2011 12:51:00 GMT</pubDate><guid>http://www.cppblog.com/eamon100/articles/155695.html</guid><description><![CDATA[简单对Filezilla Server&nbsp;0.9.39源代码进行分析，参考了《<a style="line-height: 19px; background-color: #ffffff; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; color: #1d58d1; font-size: 13px" href="http://www.cppblog.com/smagle/archive/2010/06/01/116585.html">FileZilla Server源码分析</a>&nbsp;》&nbsp;和《<a style="line-height: 19px; background-color: #ffffff; font-family: Verdana, Geneva, Arial, Helvetica, sans-serif; color: #1d58d1; font-size: 13px" href="http://hi.baidu.com/fffliuwenfei">FileZilla FTP服务器源代码分析</a>》的内容。<br />首先下载相关的文件：<br />1、FileZilla_Server-0_9_39.exe<br />2、zlib-1.2.5.tar.bz2<br />3、tinyxml_2_6_2.zip<br />安装FileZilla_Server-0_9_39.exe时，选择Source Code，如下图：<br /><img alt="" src="http://www.cppblog.com/images/cppblog_com/eamon100/17733/o_install.JPG" width="503" height="393" /><br />
<div>将相应的文件解压缩，放置在E:\FileZilla目录下：</div><img alt="" src="http://www.cppblog.com/images/cppblog_com/eamon100/17733/o_unzip.JPG" /> <br /><br />使用VS2005编译Boost库：(注：0.9.39已经移除了对Boost库的依赖，可以不用编译了)<br />
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /><span style="color: #000000">cd&nbsp;E:\FileZilla\boost_1_47_0\tools\build\v2<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" />bootstrap.bat<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" />copy&nbsp;bjam.exe&nbsp;E:\FileZilla\boost_1_47_0\<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" />cd&nbsp;E:\FileZilla\boost_1_47_0\<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" />bjam&nbsp;</span><span style="color: #000000">--</span><span style="color: #000000">prefix</span><span style="color: #000000">=</span><span style="color: #000000">E:\FileZilla\boost&nbsp;&nbsp;</span><span style="color: #000000">--</span><span style="color: #000000">build</span><span style="color: #000000">-</span><span style="color: #000000">type</span><span style="color: #000000">=</span><span style="color: #000000">minimal&nbsp;&nbsp;toolset</span><span style="color: #000000">=</span><span style="color: #000000">msvc&nbsp;&nbsp;install<br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /></span></div>会在E:\FileZilla\boost 目录下生成Boost库文件。<br /><br />使用VS2005编译Zib库：&nbsp;<br />用vs2005 打开E:\FileZilla\zlib-1.2.5\old\visualc6目录下的zlib.dsp<br />分别生成，选择LIB Debug 和 LIB Release，会在E:\FileZilla\zlib-1.2.5\old\visualc6目录下生成相应的文件 <br /><br />使用VS2005编译Filezil Server：<br />为了能在VS2005上编译FileZilla Server 源代码，将FileZilla server.vcproj 和FileZilla Server Interface.vcproj的第4行改为： <br /><br />
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /><span style="color: #000000">Version</span><span style="color: #000000">=</span><span style="color: #000000">"</span><span style="color: #000000">8.00</span><span style="color: #000000">"</span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /></span></div><br />将FileZilla server.sln的第一行和第二行改为：<br /><br />
<div style="border-bottom: #cccccc 1px solid; border-left: #cccccc 1px solid; padding-bottom: 4px; background-color: #eeeeee; padding-left: 4px; width: 98%; padding-right: 5px; font-size: 13px; word-break: break-all; border-top: #cccccc 1px solid; border-right: #cccccc 1px solid; padding-top: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /><span style="color: #000000">Microsoft&nbsp;Visual&nbsp;Studio&nbsp;Solution&nbsp;File,&nbsp;Format&nbsp;Version&nbsp;</span><span style="color: #000000">9.00</span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" />#&nbsp;Visual&nbsp;Studio&nbsp;</span><span style="color: #000000">2005</span><span style="color: #000000"><br /><img alt="" align="top" src="http://www.cppblog.com/images/OutliningIndicators/None.gif" /></span></div><br />现在可以用VS2005打开 FileZilla server.sln 项目文件了，打开时会提示找不到GFTP项目，可以忽略。<br />
<p>&nbsp;将Service设为启动项目；<br />项目属性-配置属性-C/C++-常规-附加包含目录："E:\FileZilla\zlib-1.2.5"<br />将E:\FileZilla\zlib-1.2.5\old\visualc6\Win32_LIB_Debug\zlibd.lib 拷贝到根目录(E:\FileZilla\source)下<br />项目属性-配置属性-链接器-输入-忽略特定的库：libcmtd.lib<br />项目属性-配置属性-链接器-输入-附加依赖项：atlsd.lib<br /><br />现在应该可以编译运行了。<br />修改好的源代码：<a title="source.rar" href="http://www.cppblog.com/Files/eamon100/source.rar" target="_blank">source.rar</a><br />由于Windows服务程序调试比较困难，将原程序修改为执行程序，方便调试，<br />仅供研究学习，勿用于其他用途：<a title="修改后的服务器端源代码" href="http://www.cppblog.com/Files/eamon100/MyFtpServer.rar">修改后的服务器端源代码</a></p><img src ="http://www.cppblog.com/eamon100/aggbug/155695.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/eamon100/" target="_blank">eamon</a> 2011-09-13 20:51 <a href="http://www.cppblog.com/eamon100/articles/155695.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>