欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-07-12 12:58:52
...

1.预备知识

        FileZilla Server ftp 服务器是作为 Windows 服务运行的。我们来看下安装好的服务,通过 Win + R,输入 services.msc 找到 FileZilla Server FTP server:
读懂源码系列-FileZilla Server 设计原则分析-入口分析(2)

        可以看到名为 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 library
	BOOL 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 服务器的整体结构。结构如下:
读懂源码系列-FileZilla Server 设计原则分析-入口分析(2)
        从外围代码来看,服务程序有3个线程:
            1.主线程,直到服务处于停止状态,才从 StartServiceCtrlDispatcher 函数返回,并负责执行 SCM 发送的命令;
            2.服务入口线程,执行 ServiceMain函数。函数体里边会等待一个事件,以及等待 FTP 主线执行完毕,才会返回;
            3.FTP 主线程,初始化服务器,并进入Windows 消息循环。消息循环结束后,线程结束。
       所以,我们对 FTP 服务器的分析,将集中于 FTP主线程部分。

相关标签: filezilla server