读懂源码系列-FileZilla Server 设计原则分析-入口分析(2)

来源:互联网 发布:我友网络 编辑:程序博客网 时间:2024/06/15 21:00

1.预备知识

        FileZilla Server ftp 服务器是作为 Windows 服务运行的。我们来看下安装好的服务,通过 Win + R,输入 services.msc 找到 FileZilla Server FTP server:


        可以看到名为 FileZilla Server 的服务,指向了新编译生成的 *\Debug\FileZilla Server.exe 可执行文件。如果点击启动,那么 FTP 服务器就会正式运行。
        Windows 操作系统不但提供了 services.msc 服务管理界面,而且还提供了 sc.exe 命令行工具,使得我们可以安装、删除、启动、停止和配置服务。
        此外,还有如下 API 接口,可以完成上述功能:
            OpenSCManager
            CreateService
            DeleteService
            OpenService
            StartService
            ControlService

2.入口分析

        FileZilla Server 项目源码,把 Windows 服务管理功能和 FTP 服务器本身功能结合在一起了。
        首先,找到包含 WinMain 函数的文件 Service.cpp。入口函数可以分为两个部分:
            1.根据命令行选项,完成自身的安装、删除、启动、停止和配置服务;
            2.作为服务程序入口 等待 StartServiceCtrlDispatcher 函数返回;
        注意 StartServiceCtrlDispatcher 这个函数,使得当前调用进程与服务控制管理器(SCM)进程 services.exe 建立连接,并使调用进程的主线程成为服务控制派遣线程,服务控制派遣线程之后将处理 SCM 发送的服务控制请求,直到所有服务都停止,派遣线程才返回。
        接下来看以下代码段:
const SERVICE_TABLE_ENTRY servicetable[] ={{ServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},{NULL,NULL}};BOOL success;success = StartServiceCtrlDispatcher(servicetable);
        SERVICE_TABLE_ENTRY 可以指定服务进程的服务名和服务入口,对于每一个服务入口,都会新建一个线程,用于执行服务入口。本服务只有一个入口,就是 ServiceMain。因此,主线程调用 StartServiceCtrlDispatcher 函数后,服务进程有两个线程:
        1.主线程,进入 StartServiceCtrlDispatcher ,并且在之后处理 SCM 的控制命令;
        2.新建服务线程,执行 ServiceMain;
        根据以上分析,执行流程就进入了 ServiceMain 函数:
            1.注册 SCM 控制命令处理函数 ServiceCtrlHandler ,这个函数由主线程执行,可以处理服务停止等 SCM命令;
void ServiceCtrlHandler(DWORD nControlCode){BOOL success;switch (nControlCode) {case SERVICE_CONTROL_SHUTDOWN:case SERVICE_CONTROL_STOP:nServiceCurrentStatus = SERVICE_STOP_PENDING;success = UpdateServiceStatus(SERVICE_STOP_PENDING, NO_ERROR, 0, 1, 3000);KillService();return;case 128:SendReloadConfig();break;default:break;}UpdateServiceStatus(nServiceCurrentStatus, NO_ERROR, 0, 0, 0);}
2.新建线程 ServiceExecutionThread ,作为 FTP 服务器的主线程;
DWORD ServiceExecutionThread(LPDWORD param){// initialize Winsock libraryBOOL res = TRUE;WSADATA wsaData;WORD wVersionRequested = MAKEWORD(2, 2);int nResult = WSAStartup(wVersionRequested, &wsaData);if (nResult != 0)res = FALSE;else if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {WSACleanup();res = FALSE;}if (!res){SetEvent(killServiceEvent);UpdateServiceStatus(SERVICE_STOPPED, NO_ERROR, 0, 0, 0);return 0;}CServer *pServer = new CServer;VERIFY(pServer->Create());if (!nServiceRunning)PostQuitMessage(0);MSG msg;while (GetMessage(&msg, 0, 0, 0)) {TranslateMessage(&msg);DispatchMessage(&msg);}delete pServer;WSACleanup();SetEvent(killServiceEvent);return 0;}
可以看到, FTP 主线程,是一个 Windows 消息循环。这个也是我们接下来主要分析的部分。

            3.等待服务结束事件

3.结构总结

        我们通过忽略所有细节,分析出了 FTP 服务器的整体结构。结构如下:

        从外围代码来看,服务程序有3个线程:
            1.主线程,直到服务处于停止状态,才从 StartServiceCtrlDispatcher 函数返回,并负责执行 SCM 发送的命令;
            2.服务入口线程,执行 ServiceMain函数。函数体里边会等待一个事件,以及等待 FTP 主线执行完毕,才会返回;
            3.FTP 主线程,初始化服务器,并进入Windows 消息循环。消息循环结束后,线程结束。
       所以,我们对 FTP 服务器的分析,将集中于 FTP主线程部分。

阅读全文
0 0
原创粉丝点击