对CSocket及其运行机制有了较深的理解

依靠下载的Client/Server(客户端/服务端)程序(CSocket类编写),花了三个多小时并翻阅了MSDN文档,并跟踪CSocket源码SockCore.cpp,一点体会,下文难免有不准确和疏漏之处:

客户端与服务端的通信简单来讲:服务端socket负责监听,应答,接收和发送消息,而客户端socket只是连接,应答,接收,发送消息。

1. 对于Accept的block(阻塞)机制有了一定了解,调用服务端socket的Accept后,跟踪至源码:

while(!Accept(...))
{
    if (GetLastError() == WSAEWOULDBLOCK) //  The socket is marked as nonblocking and no connections are present to be accepted.
        PumpMessage(FD_ACCEPT);
    else
        return FALSE;
}

一个看似死循环的无数次调用socket的API函数:Accept, Accept将检查Pending connection(来自Client端的正在处理的连接)队列,直到检查到一个正确的

Pending connection。消息泵函数PumpMessage将保证socket中的事件不会发生阻塞,这里至少保证所侦听的FD_ACCEPT事件不会阻塞。

2. 实战之典型问题:客户端socket要求与服务端的socket连接时,服务端的socket为什么会进入OnAccept消息函数?

socket创建时,根据socket的描述字,将socket分配和绑定至相应的传输服务供应者(个人认为应该是windows操作系统的网络传输服务进程),socket利用网络传输服务进程便可获取来自某个ip地址(客户端)的连接请求事件,然后是提供对消息的接收和发送等其它事件。

下面进入正题,把断点设到CAsyncSocket::AttachHandle开始处:

socket在创建时还调用CAsyncSocket::AttachHandle(SOCKET hSocket, CAsyncSocket* pSocket, BOOL bDead)(CSocket从CAsyncSocket派生),将socket实例句

柄和socket指针添加至当前模块状态(注1)的一个映射表变量m_pmapSocketHandle中。

接着,在AttachHandle过程中,会new一个CSocketWnd实例 - 你可以理解它是存放所有sockets的消息池(基于window消息),请仔细查看,我这里将将sockect后多加了一个s,表示创建的多个socket将共享一个消息池CSocketWnd。

当客户端socket要求与服务端的socket连接时, 此时socket将相应的事件通知消息WM_SOCKET_NOTIFY发送给CSocketWnd。你可能会问socket怎么发通告消息给CSocketWnd呢,我可要先知道CSocketWnd的指针啊?答案是该指针(实际是窗口句柄)已保存在当前线程状态的m_hSocketWindow变量中, 取之!

CSocketWnd收到事件通知消息后调用消息函数:(提示:试着在OnSocketNotify中设置断点)

LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
{
 CSocket::AuxQueueAdd(WM_SOCKET_NOTIFY, wParam, lParam);
 CSocket::ProcessAuxQueue();
 return 0L;
}

有点奇怪的代码?CSocketWnd类是作为CSocket类的友元类,这意味着它可以访问CSocket类中的保护和私有成员函数和变量,AuxQueueAdd和ProcessAuxQueue是

CSocket类的静态成员函数,明白了吧!还不明白?找本C++书看一下友元的使用方法吧!

PS: 消息参数wParam是socket的句柄,lParam是socket的事件。

ok!晕了吧,真的我都有点晕了,但还没完啊,let's go on!

ProcessAuxQueue是实质处理socket通告消息的函数,在该函数中有这样一句代码:

CAsyncSocket* pSocket = CAsyncSocket::LookupHandle((SOCKET)wParam, TRUE);

其实也就是由socket句柄得到发送事件通知消息的socket指针pSocket:从m_pmapSocketHandle中查找!

最后,WSAGETSELECTEVENT(lParam)会取出事件类型,在一个简单的switch语句中判断事件类型并调用事件处理函数。
在这里,事件类型是FD_ACCEPT,当然就调用pSocket->OnAccept了!


注:

1. 当前模块状态:用于保存当前线程和模块状态的一个结构,可以通过AfxGetThreadModule()获得。AFX_MODULE_THREAD_STATE在CSocket重新定义为_AFX_SOCK_THREAD_STATE。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章