1. 要点总结
* 2011/11/22: Client核心类图及关于XXXSink的理解
* Chat窗口中的信息如何传递到服务器
UI线程:UI上的button消息响应函数
UI线程:得到网络线程的Reactor指针pReactor
UI线程:pReactor->PostEvent(SendDataEvent),投递event,并呼叫NotifyHandler通知reactor处理event
(Win32下本质上是发Windows Message WM_WIN32_SOCKET_NOTIFY)
网络线程:接收到WM_WIN32_SOCKET_NOTIFY消息
网络线程:调用pReactor->HandleProcessEvent
网络线程:从event queue里pop出pending events,并对每一个pEvent,调用pEvent->OnEventFire
网络线程:调用Connection之类东东,将数据发给Server
2. 问题汇总
关于component/container等相关的几个问题(回顾)
Q:虚拟打印:目前文档共享使用了虚拟打印技术,但若非Windows客户端又当如何?
Q:似乎ThreadManager中对Main thread及network thread的启动与初始化放在了diagnose module,WHY ?
Q:关于只有一份ConfMain.exe运行的控制是在LaunchConf中完成,为什么要做这种控制?
Q:Client主界面上的东东如何显示出来的?比如ChatMainDlg如何嵌入到主界面上的?
Q:rtconfsess.dll的load
在load rtconfclient.dll的时候,load了
Loaded symbols for 'D:\study\uc-study-workspace\box-infoserver-trunk-20111116\bin\dlls\Debug\rtconfclient.dll'
Loaded 'C:\WINDOWS\system32\comdlg32.dll', no matching symbolic information found.
Loaded 'C:\WINDOWS\system32\winmm.dll', no matching symbolic information found.
Loaded symbols for 'D:\study\uc-study-workspace\box-infoserver-trunk-20111116\bin\dlls\Debug\rtconfsess.dll'
Loaded symbols for 'D:\study\uc-study-workspace\box-infoserver-trunk-20111116\bin\dlls\Debug\sdclient.dll'
为什么rtconfsess.dll在rtconfclient.dll后load进来?机制在哪里?
Q:IConnection, IConnProvider, IConnProviderSink之间的关系?
Q:CConfPort的职责是什么?
CRtAutoPtr<CConfConnection> m_reliable_connection;
CRtAutoPtr<CConfConnection> m_assistant_connection; // 区别了reliable与assistant connection,有何用?选择tcp/udp?
Q:在Client启动及运行的所有时间里,与Server端的交互都是通过network thread而非main(UI)thread?
Q:从语义上说,IEventQueue.PostEvent()完成了Event的投递,并且,也触发了对Event的异步处理?
3. 下步计划
4. 杂问杂记
* CRtReactorWin32Message与CRtReactorWoin32Asyncselect
OnHandleRegister的调用试验:
XXXMessage没有被调用过,XXXAsyncselect被调用了N次(为什么?),但最终client crash了,没有能启动(此时已经看到界面了,同时出现了N个线程在后台,WHY?)。
注:N个线程,很多是由其它模块启动的(如与audio有关的东东),非ThreadManager启动。ThreadManager启动的线程似乎只有一个,即网络线程。
* thread
以下thread在干嘛?
NTDLL! 7c92e514()
KERNEL32! 7c802542()
CRtConditionVariableThread::Wait(CRtTimeValue * 0x00000000) line 190 + 26 bytes
CRtEventQueueUsingConditionVariable::PopOrWaitPendingEvents(std::list<IRtEvent *,std::allocator<IRtEvent *> > & {...}, CRtTimeValue * 0x00000000, unsigned long 4294967295) line 159 + 15 bytes
CRtThreadTaskWithEventQueueOnly::OnThreadRun() line 32 + 22 bytes
CRtThread::ThreadProc(void * 0x0208c8b8) line 151 + 13 bytes
_threadstartex(void * 0x0208c9d8) line 227 + 13 bytes
KERNEL32! 7c80b729()
* CRtReactorBase::ProcessHandleEvent
(其它线程调用本线程的EventQueue.PostEvent投递Event
其它线程调用本线程的NotifyHandler通知事处理(PostMessage(hWnd, WM_WIN32_SOCKET_NOTIFY...)
本地线程EventQueue::RunEvent(对于Win32就是Windows Messaging循环),收到WM_WIN32_SOCKET_NOTIFY消息从而进入处理循环
调用 CRtReactorBase::ProcessHandleEvent)
从EventQueue里将pending events全部pop up
对所有events进行处理,调用event->OnEventFire()
NotifyHandler: NotifyHandler(NULL, IRtEventHandler::EVENTQUEUE_MASK); // WHY THIS ???有点在NotifyHandler这里搞成多次调用,死循环了?
* 跟踪WinMain,找出client与server真正开始通讯的地方
使用WireShark来跟踪
Q:如何设置Capture Filter及Display Filter?
* 当前DSW中有哪些DLL能被Build
查程序启动的时候,有哪些symbol未被load,即可知。
* 关于主线程(UI)消息循环
STRACK TRACE
CConfManage::OnConferenceJoinConfirm(int 40206, const CInfoSID & {...}, const CInfoSID & {...}, const CInfoSID & {...}, unsigned long 0) line 325
CConference::OnPingResult(int 40206, unsigned long 0, unsigned short 0, const TransportAddress * 0x00000000, const TransportAddress & {...}) line 861 + 68 bytes
CConfPing::OnReceiveData(CGcPduBase * 0x0208ba30) line 92 + 90 bytes
CPingBase::OnReceiveData(CRtMessageBlock & {...}, IConnection * 0x0208b864) line 187 + 27 bytes
CRtIMConnection::OnReceive(CRtMessageBlock & {...}, IRtTransport * 0x0208b718, CRtTransportParameter * 0x00000000) line 890 + 50 bytes
CRtEventOnReceive::OnEventFire() line 375 + 50 bytes
CRtEventQueueBase::ProcessOneEvent(IRtEvent * 0x02087d58) line 229 + 12 bytes
CRtEventQueueBase::ProcessEvents(const std::list<IRtEvent *,std::allocator<IRtEvent *> > & {...}) line 217
CRtReactorBase::ProcessHandleEvent(void * 0xffffffff, long 256, int 0, int 1, int 0) line 324 + 18 bytes
CRtReactorWin32Message::Win32SocketWndProc(HWND__ * 0x000b0dce, unsigned int 1058, unsigned int 4294967295, long 256) line 115
USER32! 77d18734()
USER32! 77d18816()
USER32! 77d189cd()
USER32! 77d196c7()
WTL::CMessageLoop::Run() line 468 + 15 bytes
Run(char * 0x00151f28, int 1) line 207 + 11 bytes
LaunchConf(HINSTANCE__ * 0x00400000, char * 0x00151f28, int 1) line 318 + 13 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00151f28, int 1) line 85 + 20 bytes
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 7c817077()
注:说明在系统启动后,主消息循环的处理中居然有Win32SocketWndProc,如何发生的?
Q:是否在某个时间,把当前hWnd通过某种方式与Win32SocketWndProc相对应的Windows Class注册了?
Q:或者是因为在某个线程中,其消息对列实际上可以给多个窗口的WindowsMessagingHandler使用? I think so.
所以,主线程(UI)其实至少有两人Windows Handler,一个是WTL用于处理UI的,一个是自已定义的Win32SocketWndProc,用于处理当前线程的Reactor。也就是说,当网络线程从网络获得输入的时候,它要通过PostEvent/NotifyHandler来通知主线程的Win32SocketWndProc,以响应来自Server端的信息。
* client/server之间是同步还是异步?
Client在向Server发送了Ping请求(或Leave Conference)后,是否要等到Server应答后再进行以下动作?
从代码跟踪看,似乎并非如此?
* 主线程与网络线程通信方式细节?
发出数据到server?
从Server接收数据?
都是通过EventQueue完成的?
主线程会直接向Server收发数据吗?( I think not)
# SendData的本质:向网络线程Post一个SendEvent
STACK TRACE:
CRtEventQueueBase::PostEvent(IRtEvent * 0x0211c110, IRtEventQueue::EPriority EPRIORITY_NORMAL) line 132
CRtEventQueueUsingMutex::PostEventWithOldSize(IRtEvent * 0x0211c110, IRtEventQueue::EPriority EPRIORITY_NORMAL, unsigned long * 0x0012f900) line 267 + 16 bytes
CRtReactorBase::PostEvent(IRtEvent * 0x0211c110, IRtEventQueue::EPriority EPRIORITY_NORMAL) line 517 + 23 bytes
CRtTransportThreadProxy::SendData(CRtMessageBlock & {...}, int 0, int 0, IRtTransport::DataPriority DP_MEDIUM, int 0) line 128 + 59 bytes
CODE:
RtResult CRtTransportThreadProxy::SendData(...) {
......
return m_networkThread->GetEventQueue()->PostEvent(sendDataEvent);
......
}
* IRtEvent:理解系统功能的关键!
找出所有CEventXXX?
* RtResult CRtReactorBase::ProcessHandleEvent()
从EventQueue里取出pending events
Handle Each Event: pEvent->OnFireEvent()
Notify Handler ??? // 也就是在当前pending events都处理后,notify handler。 For what ???
注1:这个方法需要被Reactor的客户代码所调用,WHY NOT DEFINED IN IRtReactor?
注2:这个方法逻辑非常重要,需要仔细再研读下。
* 单线程与多线程
Client实际上是多线程的,但提供给程序员的接口表现成单线程的。
Q:哪些地方做了这项工作?
* CRtTransportTcp
这是实际发生Input/output的地方,使用SOCKET。
CRtTransportTcp(IRtReactor *pReactor);
Q:有reactor在constructor中,但哪里在使用?
* CConfConnetion
CRtAutoPtr<CConference> m_conference;
CRtAutoPtr<IConnection> m_work_connection;
CPackageQueue m_queues[4];
注:最重要的是,这不是一个原始的Connection,有conference的语义,表现在有一个package queue,每一个优先级有一个,发送的时候,高优先级先发,再发低优先级,故在此能对优先级进行控制,代码如下:
void CConfConnection::SendData_i()
{
if (!m_canSend)
{ return; }
for (int i = 0; i < 4; i++)
{
...
while (currentPackage = m_queues[i].GetFirst(TRUE))
{
...
RtResult ret = SendBufData(*currentPackage);
...
}
}
* UI thread 如何接收Network thread(也就是server)的信息?
CRtEventOnReceive::OnEventFire() line 375 + 50 bytes
CRtEventQueueBase::ProcessOneEvent(IRtEvent * 0x020b0780) line 229 + 12 bytes
CRtEventQueueBase::ProcessEvents(const std::list<IRtEvent *,std::allocator<IRtEvent *> > & {...}) line 217
CRtReactorBase::ProcessHandleEvent(void * 0xffffffff, long 256, int 0, int 1, int 0) line 324 + 18 bytes
^ 这一步,从event queue里pop up所有pending events并进行处理
CRtReactorWin32Message::Win32SocketWndProc(HWND__ * 0x001007da, unsigned int 1058, unsigned int 4294967295, long 256) line 115
^ 在这一步,肯定是因为network thread向UI Thread发送了Event,并Notify Handler 2011/12/04
^ 从WM_WIN32_SOCKET_SELECT的代码应该能跟踪到
USER32! 77d18734()
USER32! 77d18816()
USER32! 77d189cd()
USER32! 77d196c7()
WTL::CMessageLoop::Run() line 468 + 15 bytes
Run(char * 0x00151f28, int 1) line 207 + 11 bytes
LaunchConf(HINSTANCE__ * 0x00400000, char * 0x00151f28, int 1) line 318 + 13 bytes
WinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char * 0x00151f28, int 1) line 85 + 20 bytes
WinMainCRTStartup() line 330 + 54 bytes
KERNEL32! 7c817077()
* 接上步,从WM_WIN32_SOCKET_SELECT设断点,跟踪
STACK TRACE
CRtSocketBase::SendV(const iovec * 0x00f28ef0, unsigned long 1) line 306
CRtTransportTcp::SendData(CRtMessageBlock & {...}, int 1, int 0, IRtTransport::DataPriority DP_MEDIUM, int 1) line 152 + 20 bytes
CRtConnRlbTcpClient::SendConnReq() line 664 + 48 bytes
CRtConnRlbTcpClient::OnConnectIndication(int 0, IRtTransport * 0x0208b5dc, IRtAcceptorConnectorId * 0x0208b250) line 467
CRtConnectorWrapper::OnConnectIndication(int 0, IRtTransport * 0x0208b5dc, IRtConnectorInternal * 0x0208b2bc) line 150 + 30 bytes
CRtConnectorTcpT<CRtConnectorWrapper,CRtTransportTcp,CRtSocketStream>::OnOutput(void * 0x00000668) line 141
CRtReactorBase::ProcessHandleEvent(void * 0x00000668, long 2, int 0, int 0, int 0) line 387 + 23 bytes
CRtReactorWin32Message::Win32SocketWndProc(HWND__ * 0x00020f80, unsigned int 1057, unsigned int 1640, long 16) line 95
USER32! 77d18734()
USER32! 77d18816()
USER32! 77d189cd()
USER32! 77d196c7()
CRtReactorWin32Message::RunEventLoop() line 262 + 15 bytes
CRtThreadReactor::OnThreadRun() line 67 + 19 bytes
CRtThread::ThreadProc(void * 0x02087ef0) line 151 + 13 bytes
_threadstartex(void * 0x02087fa0) line 227 + 13 bytes
KERNEL32! 7c80b729()
Q:这里似乎network thread没有向UI thread发东东,可能因为这是一个connect动作?connect应该是从UI Thread发起的(但如何放进Asyncselect的呢?)试下read动作。
* 接上步,尝试input事件流
STACK TRACE:
CRtAcceptorConnectorSinkThreadProxyT<CRtConnectorThreadProxy>::OnConnectIndication(int 0, IRtTransport * 0x0208b530, IRtAcceptorConnectorId * 0x0208b420) line 146
^ 在这里,终于看到对UI线程调用PostEvent,代码见后。
CRtConnRlbTcpClient::OnRecvConnResp() line 613 + 66 bytes
CRtConnRlbTcp::OnReceive(CRtMessageBlock & {...}, IRtTransport * 0x0208b724, CRtTransportParameter * 0x00000000) line 254 + 17 bytes
CRtTransportTcp::OnInput(void * 0x00000664) line 70 + 52 bytes
CRtReactorBase::ProcessHandleEvent(void * 0x00000664, long 4, int 0, int 0, int 0) line 381 + 23 bytes
CRtReactorWin32Message::Win32SocketWndProc(HWND__ * 0x000c082c, unsigned int 1057, unsigned int 1636, long 1) line 95
USER32! 77d18734()
USER32! 77d18816()
USER32! 77d189cd()
USER32! 77d196c7()
CRtReactorWin32Message::RunEventLoop() line 262 + 15 bytes
CRtThreadReactor::OnThreadRun() line 67 + 19 bytes
CRtThread::ThreadProc(void * 0x02087ef0) line 151 + 13 bytes
_threadstartex(void * 0x02087fa0) line 227 + 13 bytes
KERNEL32! 7c80b729()
CODE:
template <class ThreadProxyType>
class CRtAcceptorConnectorSinkThreadProxyT
: public IRtAcceptorConnectorSink
CRtAcceptorConnectorSinkThreadProxyT::OnConnectIndication() {
....
CEventOnConnectIndication<ThreadProxyType> *pEvent =
new CEventOnConnectIndication<ThreadProxyType>(
m_pThreadProxy,
aReason,
pTransportThreadProxy,
m_pThreadProxy);
m_pThreadProxy->m_pThreadUser->GetEventQueue()->PostEvent(pEvent);
^ main(UI) thread !!!
....
}