Code Tracing

来源:互联网 发布:图册排版设计软件 编辑:程序博客网 时间:2024/05/17 07:21

我们来看看在用户登陆时关于文件业务方面都做了哪些事情?

首先,连接到服务器,调用CPilotLinkerConnectServer()连接到服务器。在ConnectServer中,实际上让全局变量g_pSocketClient连接到服务器,同时让全局变量g_pilotCommunicate接管g_pSocketClient(我早就说过这样的非接管不安全,在普通的工程中不可能实现完全接管,除非你自己是在创建自己的操作系统平台或者虚拟机平台),并且让g_socketSink成为g_pSocketClient的监听者。所以记住一点,g_pilotCommunicate实际上是对g_pSocketClient进行操作,而g_socketSink响应g_pSocketClient的事件。

 

其次,调用CPilotFileInitFile()函数启动文件服务器,用户为了能够彼此之间相互传递文件,每个用户端都有一个文件服务器。

 

接下来,我们在看看服务器端的初始化。

这里值得说的只有一点:即PilotWatch的启动。在PilotWatchstart函数中,创建了服务器端socket,同时PilotWatch成为该socket的监听者。

 

服务器的初始化完成了,它就开始监听。前面说客户端在登陆时连接到服务器,服务器是如何接收客户端的连接请求的?服务器监听到连接请求,接受请求,生成新的socket实例,即new CXIOCPClientSocket,我们称这个实例为连接socket 这个连接socket和客户端socket实例构成了一个session对话。同时生成新的PilotSailer实例,调用PilotSailerclientLand函数,使PilotSailer实例成为连接socket的监听者,同时该实例接管这个连接socket。这里可以看见,对每个客户端来说,服务器端有一个连接socket与之对应,而这个连接socket又有一个PilotSailer的监听者实例。

 

现在我们来谈谈peer是如何交互的,业务太多,不可能逐个说明,这里选取文件搜索、文件下载,这个两个业务合并成一个完整的业务。

 

当客户端从界面得到要搜索的内容,调用CPilotFileSearchFile进行搜索文件。我们一步步看这个过程是如何完成的:从发送搜索请求到服务器,服务器连接到数据库进行搜索,最后返回结果至客户端,客户端将其显示在UI上。

CPpilotFileSearchFile函数中,主要完成了两个工作。第一,将“搜索条件”打包,用g_pilotCommunicate发送给服务器,实质是用客户端socket全局变量g_pSocketClient直接发送。我们都知道在服务器端,对于每个客户,都有一个负责和此客户连接通信的socket,但不是这个socket直接接受数据,而是该socket的监听者,即PilotSailer实例。因为它监听这个socket的事件。 好了,到这里,PilotSailer接受到了数据,接着根据该数据包的头部转发该数据包。因为这个一个关于搜索文件的数据包,最后转发执行searchFile函数。在searchFile中主要完成两个工作:第一,生成线程实例,连接到数据库搜索文件,并将搜索结果打包发送到客户端(至于客户端是如何响应接受数据的,我们等会再说)。第二,生成搜索回复包,并发送到客户端。注意:这里有两个数据包,一个是回复,表明搜索是否成功;另一个是搜索结果的详细内容,在美一个搜索记录里包含了[符合搜索条件的文件]的信息,包括文件的“URL”,HashID,长度等。

 

好,下面来看客户端如何接受数据包并解包!

对于服务器端发来的两个数据包,回复包由全局变量g_socketSink主动接受,那搜索结果是如何接受的呢?先前讲过,在客户端g_socketSinkg_pClientSocket的监听者,当搜索结果数据包到达时,g_socketSink进行处理。g_socketSink是如何进行处理的呢?g_socketSink 响应数据包到达事件的OnReceive函数里面,调用了dispatchMessage(数据包),即将数据包转发。好,到这里,我们停一下,但不是休息。在g_socketSink维护了多个成员变量,分别是m_pPilotLinkerm_pPilotUserm_pPilotFilem_pPilotBankm_pPilotMessagem_pPilotChatg_socketSink为何要维护这么多的成员?这里我简单解释一下,g_socketSink的原型是CSocketSink,它扮演的是一个Façade,即模式中的Façade(这里我想说一句,思想没有对错,关键在于是否适合于特定的情形,技术没有新旧,关键是用对在点上。Façade很好,但这里coder用法糟糕。Façade的引进是简化系统内部的复杂逻辑,对外提供简易的访问接口,而这里的对外接口不但不简易,反而变本加厉,这是对技术的滥用)。休息停止,我们继续,下面很好理解,g_socketSink将数据包转发给了m_pPliotFile,为什么呢?m_pPilotFile的原类型是CPilotFile,而CPilotFile是运行在客户端的文件服务器。好,最后一步,显式调用m_pPilotFileDispatchMessage(数据包),将数据包传递给搜索结果对话框,通知搜索结果对话框显示搜索结果。That’s allThank you

        

         文件搜索的直接目的是下载文件。文件是如何下载的,同时它的过程又是怎样的复杂?

         下面我们来看看。

         UI出发,直击底层。在搜索结果对话框中,下载选中的文件,这里我们假设下载一个特定的文件,因为多个文件的下载是一个循环兼并行处理的过程,简单易懂。下载选中的文件,转入到第一个非UI调用入口:显然应该是文件服务器CPilotFile,调用theApp.m_pFile实例的DownloadFile(文件的URL 待存储目录)。这里URL的实际内容不是通常说的URL。这里URL是一个字符串,由类型、HashID、文件路径、文件大小组成。在DownloadFile函数里,创建新的下载任务CFileDownload,即new CFileDownload,我们称这个下载任务为CFileDownload实例。CFileDownload实例从URL进行解析出文件的类型、HashID、文件路径、文件大小,CFileDownload持有这些信息。启动CFileDownload实例的Start。开始进行下载。继续跟踪下去,在start中有3路分发:第一,P2P下载;第二,HTTP下载;第三,FTP下载;我们转入到P2P下载中去,即StartP2PDownload()。一条简单的线程实例将我们带到ConnectProc中去。

ConnectProc中调用CPilotFileGetFileUser,发送“请求拥有该文件的对端的号码”数据包(PCMD_GETFILEUSER)到服务器,服务器端对应的PilotSailer监听到数据包(为何对应的PilotSailer能监听到该数据包,这前面已经讲过了:对于每个客户端,在服务器端有一个负责与其通信的socket实例,而该实例有对应的监听者PilotSailer实例),PilotSailer返回拥有该文件的客户端号码。好了,到这里要下载文件的客户端可以连接对端了吗?显然没有,因为对端的号码不能提供任何连接信息。

依然回到CFileDownloadConnectProc()中,显式调用CFileDownload实例的ConnectUser()函数。在onnectUser()的实现代码中,我们可以看见,刚才获得的对端号码被利用起来了:以对端号码为参数,调用CPilotLinkerGetLinkInfo()函数获取对端的IPPort。利用对端的IPPort创建一个LPDOWNLOAD_FILE实例(这是一个结构体,持有socket)。将该实例持有的socket连接到对端,同时将CFileDownload设置为socket的监听者。到这里客户端已经取得和对端的连接。

 

下面我们有必要看看对端是如何接受外来连接请求的!

同样对端客户端是以CPilotFile文件服务器在监听外来请求。至于这个文件服务器的初始化,在一开始已经讲了。现在看CPilotFileAccept)函数。接受到外来连接请求后,创建一个CXAsyncSelectSocket实例,我们称之为socket连接实例,并且获取对方的IPPort。借助socket连接实例、对方的IPPort,创建一个CFileSupply实例,即new CFileSupply,我们称之为CFileSupply实例。CFileSupply的构造函数中我们发现,CFileSupply实例接管了socket连接实例,同时将CFileSupply实例自身设置为socket连接实例的事件监听者,并发送数据包给要下载文件的客户,告诉对方服务准备好了,即PCMD_Server_Ready

 

原创粉丝点击