• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大漠落日

            while(!dead) study++;
            posts - 46, comments - 126, trackbacks - 0, articles - 0
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            FileZilla Server源碼分析(3)

            Posted on 2010-06-10 13:38 亂78糟 閱讀(2337) 評(píng)論(1)  編輯 收藏 引用 所屬分類(lèi): 開(kāi)源
            這是分析的第三節(jié),上一節(jié)主要講了一些和socket基礎(chǔ)操作相關(guān)的代碼,本節(jié)將分析核心代碼。

            Service.cpp   系統(tǒng)服務(wù)程序

            FileZillaServer可以選擇是否注冊(cè)成windows的服務(wù)程序,而這個(gè)服務(wù)程序的代碼就是由service.cpp文件實(shí)現(xiàn)的。
            WinMain是它的入口函數(shù),在WinMain里依次完成了下面幾項(xiàng)任務(wù):
            1. 參數(shù)解析
            2. 初始化某些數(shù)據(jù)比如端口
            3. 由SCM(服務(wù)控制管理器)啟動(dòng)服務(wù),入口為ServiceMain函數(shù);如果服務(wù)不存在,進(jìn)入步驟4
            4. 根據(jù)參數(shù)設(shè)置服務(wù),例如安裝、啟動(dòng)、卸載等。
            ServiceMain注冊(cè)ServiceCtrlHandler來(lái)處理服務(wù)的控制代碼,在回調(diào)函數(shù)ServiceCtrlHandler中自定義了128號(hào)控制碼,用于向窗口"FileZilla Server Helper Window"發(fā)送重新讀取配置的自定義消息WM_FILEZILLA_RELOADCONFIG
            serviceMain注冊(cè)Handler成功之后,就啟動(dòng)自己的工作線(xiàn)程ServiceExecutionThread,線(xiàn)程里創(chuàng)建了CServer對(duì)象,然后實(shí)際流程交由CServer。之后進(jìn)入線(xiàn)程的消息循環(huán)并等待killServiceEvent信號(hào)以退出線(xiàn)程終止服務(wù)。
            Service.cpp中KillService函數(shù)中有一個(gè)變量hMainWnd,它是在stdafx.h中聲明的,它具體是哪個(gè)窗口的句柄,干什么用,現(xiàn)在還是一無(wú)所知。


            Server.*  真正的帶頭大哥

            打開(kāi)Server.h文件,開(kāi)頭就可以看到許多類(lèi)的前置聲明,類(lèi)中聲明了眾多上一節(jié)提到的相關(guān)類(lèi)對(duì)象(或集合如list),CServer類(lèi)把所有核心類(lèi)(線(xiàn)程和socket)集中起來(lái)使用。
            上面已經(jīng)提到Service的工作線(xiàn)程中調(diào)用了CServer的Create函數(shù),我們就先從這里入手吧。
            Create一開(kāi)始就創(chuàng)建了一個(gè)窗口,標(biāo)題為"FileZilla Server Helper Window",呵呵,是不是很熟悉?然后將這個(gè)窗口的句柄賦給全局變量hMainWnd,至此,終于大致了解了服務(wù)的框架骨骼。
            之后又是一大堆初始化操作,包括兩個(gè)定時(shí)器,需要特別注意的是創(chuàng)建服務(wù)線(xiàn)程CServerThread時(shí)候的提供的參數(shù)WM_FILEZILLA_SERVERMSG + index,這個(gè)參數(shù)用以線(xiàn)程間通信,再上一節(jié)已經(jīng)有過(guò)描述,稍后再具體分析。
            在往下調(diào)用了CreateListenSocket函數(shù),這個(gè)函數(shù)根據(jù)Options類(lèi)中獲取的port、bindip、enablessl等參數(shù)創(chuàng)建監(jiān)聽(tīng)ftp客戶(hù)端連接的CListenSocket對(duì)象指針,并保存到m_ListenSocketList中。這里有一個(gè)很重要的函數(shù)ShowStatus,它的任務(wù)是將信息發(fā)送給admin窗口和記錄到log中。
            最后調(diào)用CreateAdminListenSocket函數(shù)創(chuàng)建監(jiān)聽(tīng)admin客戶(hù)端的socket,并存入m_AdminListenSocketList中。

            CServer類(lèi)的分析暫時(shí)中斷一下,我們來(lái)分析上面涉及到的幾個(gè)相關(guān)類(lèi):CServerThread,CListenSocket,CControlSocket,CTransferSocket。

            CServerThread繼承自CThread,構(gòu)造函數(shù)有個(gè)int型參數(shù),用來(lái)標(biāo)識(shí)具體哪個(gè)線(xiàn)程的消息。注意,CThread本身并不是一個(gè)直接繼承于任何線(xiàn)程類(lèi)的類(lèi),它只是負(fù)責(zé)創(chuàng)建并管理線(xiàn)程的類(lèi)。m_sInstanceList是static的成員變量,被所有的CServerThread對(duì)象共享,而且這個(gè)list存儲(chǔ)的第一個(gè)值用于管理SL,為了標(biāo)識(shí)它,作者又添加了一個(gè)BOOL成員變量m_bIsMaster,同樣還有一個(gè)static臨界區(qū)變量m_GlobalThreadsync用來(lái)同步它。如果當(dāng)前對(duì)象是master,那么它還擁有一個(gè)用于實(shí)現(xiàn)PASV模式的CExternalIpCheck的類(lèi)對(duì)象m_pExternalIpCheck,缺省值是不采用PASV的。

            對(duì)CServerThread的重要的幾個(gè)Public成員函數(shù)分析一下功能:
            • GetExternalIP : 調(diào)用m_pExternalIpCheck獲取PASV的ip
            • AddSocket:給自己發(fā)送一個(gè)線(xiàn)程消息,該消息在OnThreadMessage函數(shù)中被處理,用來(lái)添加(SSL)socket連接

            對(duì)CServerThread重要的幾個(gè)非public成員函數(shù)分析一下功能:
            • AddNewSocket:將sokcet handle綁定到新new的CControlSocket對(duì)象socket上,并為當(dāng)前socket分配一個(gè)唯一的用戶(hù)ID。分配函數(shù)CalcUserID不算高效,尤其是連接用戶(hù)數(shù)量比較大的時(shí)候再分配尤其明顯。之后調(diào)用SendNotification準(zhǔn)備發(fā)送包含連接用戶(hù)的信息的消息給CServer,最后向連接的用戶(hù)發(fā)送歡迎信息。
            • SendNotification:這個(gè)函數(shù)將需要發(fā)送的數(shù)據(jù)加入待發(fā)送list中,最牛的是它可以自動(dòng)調(diào)節(jié)發(fā)送的效率。不過(guò)我發(fā)現(xiàn)一處小BUG,可能作者自己也沒(méi)有注意到,這兩處設(shè)置線(xiàn)程優(yōu)先級(jí)貌似反了:
            else if (m_pendingNotifications.size() > 150 && m_throttled < 2)
            {
                SetPriority(THREAD_PRIORITY_LOWEST);
                m_throttled 
            = 2;
            }
            else if (m_pendingNotifications.size() > 100 && !m_throttled)
            {
                SetPriority(THREAD_PRIORITY_BELOW_NORMAL);
                m_throttled 
            = 1;
            }

            • OnThreadMessage:線(xiàn)程消息處理函數(shù),如添加刪除用戶(hù),解析命令,傳輸,控制,計(jì)時(shí)器等。

            CListenSocket類(lèi)功能很簡(jiǎn)單,如果一個(gè)連接被accept,那么從服務(wù)線(xiàn)程CServerThread列表中找到負(fù)載最小的線(xiàn)程,然后調(diào)用的AddSocket函數(shù),將這個(gè)連接交給這個(gè)CServerTread管理。

            CControlSocket類(lèi)負(fù)責(zé)與客戶(hù)端交互。
            它有一個(gè)int型成員變量m_antiHammeringWaitTime,用來(lái)防止用戶(hù)攻擊(即無(wú)限次嘗試登錄),例如某用戶(hù)在60秒內(nèi)連續(xù)嘗試登錄10次失敗,那么就把這個(gè)用戶(hù)加入ban列表中,比如3000秒內(nèi)拒絕再次登錄等。AntiHammerIncrease 函數(shù)中對(duì)這個(gè)變量的算法沒(méi)看明白
            if (m_status.hammerValue > 2000)
                m_antiHammeringWaitTime 
            += 1000 * (int)pow(1.3, (m_status.hammerValue / 400- 5);
            在用戶(hù)登錄的時(shí)候就去檢測(cè)是否是“攻擊”,代碼如下:
                BOOL bResult = GetPeerName((SOCKADDR*)&sockAddr, &nSockAddrLen);
                    
            if (bResult)
                        m_pOwner
            ->AntiHammerIncrease(sockAddr.sin_addr.s_addr);

                    
            if (m_pOwner->m_pAutoBanManager->RegisterAttempt(htonl(sockAddr.sin_addr.s_addr)))
                    {
                        Send(_T(
            "421 Temporarily banned for too many failed login attempts"));
                        ForceClose(
            -1);
                        
            return FALSE;
                    }

            PassCommand函數(shù)處理所有的命令,如USER、LIST、PASV、STOR等。當(dāng)收到STOR命令時(shí),如果是PASV模式,那么調(diào)用m_transferstatus.socket->PasvTransfer(),否則新建一個(gè)CTransferSocket套接字賦給m_transferstatus.socket,然后調(diào)用SendTransferinfoNotification發(fā)送TRANSFERMODE_RECEIVE消息。不管哪種方式,最后還是通過(guò)調(diào)用CTransferSocket的InitTransfer函數(shù)實(shí)現(xiàn)文件傳輸。


            好了,現(xiàn)在讓我們恢復(fù)現(xiàn)場(chǎng)。
            CServer類(lèi)的消息處理函數(shù)WindowProc,處理了各種消息,其中重要的是WM_DESTROYWM_FILEZILLA_SERVERMSG。前者通知并等待所有線(xiàn)程退出,關(guān)閉socket,銷(xiāo)毀資源,殺死定時(shí)器,做的都是清理工作。后者根據(jù)服務(wù)線(xiàn)程發(fā)送來(lái)的消息進(jìn)入函數(shù)OnServerMessage中,這個(gè)函數(shù)處理了所有服務(wù)管理的消息。可以看到,很多消息最后都是通過(guò)m_pAdminInterface->SendCommand(2, 3, buffer, len)這句發(fā)送出去。CAdminInterface類(lèi)管理CAdminSocket類(lèi)的指針列表,SendCommand其實(shí)是調(diào)用CAdminSocket的SendCommand將消息發(fā)送出去。函數(shù)中對(duì)admin socket做了自動(dòng)管理,如果操作失敗,就自動(dòng)移除該socket。
            CheckForTimeout每10秒由CServer的定時(shí)器調(diào)用一次,檢測(cè)admin socket是否超時(shí),如果超時(shí),自動(dòng)移除。CAdminSocket收到數(shù)據(jù)并解析成功之后,最終交由CServer的ProcessCommand處理,該函數(shù)再一次根據(jù)Options里的設(shè)置對(duì)線(xiàn)程、socket進(jìn)行一次校驗(yàn)和調(diào)整。

            我個(gè)人對(duì)ProcessCommand和SendCommand函數(shù)參數(shù)中的type或nID為int型有微議,因?yàn)檫@兩個(gè)參數(shù)實(shí)際只占用了不到8個(gè)字節(jié),寫(xiě)為int不利于理解,如果改成int8一眼就能看出來(lái)這個(gè)參數(shù)具體占用幾個(gè)字節(jié)。
            BOOL CAdminSocket::SendCommand(int nType, int nID, const void *pData, int nDataLength)
            {
                
            /**/
                t_data data;
                data.pData 
            = new unsigned char[nDataLength + 5];
                
            *data.pData = nType;     //nType目前版本只要不為0就是合法的協(xié)議類(lèi)型,代碼中用到了1和2
                
            *data.pData |= nID << 2//nType和nID合用一個(gè)8字節(jié)
                data.dwOffset = 0;
                memcpy(data.pData 
            + 1&nDataLength, 4);
                
            /**/
            }

            下面重點(diǎn)分析一下ProcessCommand這個(gè)函數(shù),用偽代碼比較直觀(guān)。
            BOOL CServer::ProcessCommand(CAdminSocket *pAdminSocket, int nID, unsigned char *pData, int nDataLength)
            {
                
            switch(nID)
                { 
                
            case 2:
                   
            if (!nDataLength)
                       
            //獲取服務(wù)器狀態(tài)
                   else
                       
            //設(shè)置服務(wù)器狀態(tài)并獲取
                   else
                       
            //send error:wrong protocol type
                   break;
                 
            case 3:
                    
            if (!nDataLength)
                       
            //send error
                    else if (*pData == USERCONTROL_GETLIST)
                       
            //計(jì)算并格式化所有已連接用戶(hù)的信息到unsigned char *buffer中并發(fā)送給admin
                       
            //這些數(shù)據(jù)顯示在admin UI下方的user list中
                    else if (*pData == USERCONTROL_KICK || *pData == USERCONTROL_BAN)
                        
            //*pData共5個(gè)字節(jié),第一個(gè)為具體協(xié)議類(lèi)型,后四個(gè)為userID。
                        
            //根據(jù)協(xié)議對(duì)userID進(jìn)行操作,kick或者Ban掉。
                    else
                         
            //send error: wrong protocol type
                     break;
                
            case 5:
                     
            if (!nDataLength)
                        
            //讀取基本配置然后發(fā)送給admin
                     else if (*m_pOptions)
                        
            //解析配置字符串,創(chuàng)建初始化或調(diào)整CServerThread
                        
            //CreateListenSocket
                        
            //創(chuàng)建admin監(jiān)聽(tīng)sockets
                      break;
                 
            case 6:
                      
            if (!nDataLength)
                          
            //讀取user和group的權(quán)限配置
                      else
                          
            //解析權(quán)限配置發(fā)送給admin
                      break;
                 
            case 8:
                      pAdminSocket
            ->SendCommand(18, NULL, 0);
                      
            break;
                  
            default:
                      
            //send error: unknow command
                }
                
            return true;
            }

            這一節(jié)涵蓋了眾多核心代碼,上面的分析相對(duì)來(lái)說(shuō)還是比較粗略,所以,后面幾節(jié)在對(duì)這些粗略和遺漏部分在做更為詳細(xì)深入的挖掘,本節(jié)到這里就結(jié)束了。
            因?yàn)槎际强创a時(shí)臨時(shí)寫(xiě)入筆記的,所有的分析都很雜亂,希望以后我有時(shí)間可以畫(huà)一些圖,重新做一次整理。
            2010-7-22補(bǔ)充
            圖隨便畫(huà)了幾張,鏈接在此

            PS: 本來(lái)上周就可以貼出來(lái)了,可是因?yàn)榘惭bMAC系統(tǒng)造成C盤(pán)WINDOWS系統(tǒng)數(shù)據(jù)破壞無(wú)法啟動(dòng),重裝系統(tǒng)導(dǎo)致筆記丟失,這里只能補(bǔ)上,拖后了一周左右。

            Feedback

            # re: FileZilla Server源碼分析(3) [未登錄](méi)  回復(fù)  更多評(píng)論   

            2012-03-19 16:34 by ww
            SendNotification:這個(gè)函數(shù)將需要發(fā)送的數(shù)據(jù)加入待發(fā)送list中,最牛的是它可以自動(dòng)調(diào)節(jié)發(fā)送的效率。不過(guò)我發(fā)現(xiàn)一處小BUG,可能作者自己也沒(méi)有注意到,這兩處設(shè)置線(xiàn)程優(yōu)先級(jí)貌似反了

            這個(gè)他在注釋中已經(jīng)寫(xiě)了
            // Check if main thread can't handle number of notifications fast enough, throttle thread if neccessary


            是要讓主系程 降低優(yōu)先級(jí)的 并沒(méi)有作者所說(shuō)的反了
            色偷偷久久一区二区三区| 精品人妻伦一二三区久久| 久久国产亚洲高清观看| 91精品国产综合久久香蕉| 国产精品中文久久久久久久| 久久久久久久精品成人热色戒 | 亚洲国产精品热久久| 午夜精品久久久久9999高清| 久久久久久九九99精品| 久久久久亚洲av成人无码电影| 无码久久精品国产亚洲Av影片| 九九久久精品国产| 久久久久亚洲av无码专区导航| 麻豆久久| 久久精品国产亚洲7777| 99久久精品国内| 人妻丰满AV无码久久不卡| 欧美久久久久久精选9999| 国产精品久久网| 色综合久久久久久久久五月| 亚洲国产日韩综合久久精品| 精品久久久久中文字| 国产精品一区二区久久| 久久青青草原精品国产| 亚洲人成伊人成综合网久久久| 亚洲欧美日韩精品久久亚洲区| 久久www免费人成看国产片| 91精品国产91久久综合| 漂亮人妻被黑人久久精品| 国内精品久久久久久久久电影网| 亚洲精品美女久久久久99小说| 很黄很污的网站久久mimi色| 一本色道久久88加勒比—综合| 久久er99热精品一区二区| 无码日韩人妻精品久久蜜桃 | 久久无码AV一区二区三区| 久久久久一级精品亚洲国产成人综合AV区| 99久久99久久精品免费看蜜桃| 久久国产色AV免费观看| 久久久久久狠狠丁香| 久久福利青草精品资源站|