CAsyncSocket ,CSocket多线程环境
我接触Windows网络编程有一段时间了,对Windows网络编程,Windows网络编程模型和Linux网络编程模型有一个很不一样的地方,这就是异步选择模型。异步选择模型和普通的选择模型有着本质的区别,这个模型利用了Windows的窗口消息机制 。
在Linux网络编程里,最常用的是select模型,调用select函数后,线程进入阻塞状态,直到超时或socket的相关操作有可用性,比如可以网络对端发来数据,这时系统内核会告知现在SOCKET现在可读,此时select函数返回,线程继续执行。
WINDOWS网络里也提供这样的select调用,但是为了更好的利用WINDOWS消息机制,WINDOWS在传统Beckly socket的基础上进行了高度扩展,称为WINSOCK。WINSOCK里提供了异步选择机制,它的原理是将一个网络通知事件作为消息传递到一个接收窗口,利用WINDOWS的消息处理机制,将网络事件转给一个窗口过程来处理,程序员通过写这个窗口过程(实际上是个大的SWITCH-CASE语句)来实现对网络事件的处理。
MFC类是对WINDOWS API的封装,当然也少不了对WINSOCK API 的封装。
MFC对WINSOCK API的第一层封装类是 CAsyncSocket,他不仅封状了API,而且还让程序员省去了建立窗口、注册窗口消息和写窗口过程的麻烦,提供了回调接口,程序员只需要重写感兴趣的虚函数,就可以实现异步调用,这听起来似乎很方便,但是MSDN却说:使用CAysncSocket需要自己管理阻塞,自己处理网络字节顺序等。因为我们执行CAysncSocket的每一次异步调用,都不能保证程序已按照我们的意愿正确执行了。比如你调用Send来发送网络数据,你得到的返回值是WSAEWOULDBLOCK,表示你的操作是异步的,不能立刻返回,告诉你不用担心,但是以后是不是真正执行了,你是不知道的。要管理这样的过程,就需要你在每次调用后再进行检查(具体方法不详)。
CSocket是由CAysncSocket派生而来的,MSDN说CSocke提供的是同步阻塞模式的操作,我觉得严格意义上讲,从源代码上看,也是异步选择模式,不过他重写了操作函数,使得每次调用基类的异步操作后,都进行了阻塞管理,实际上自己就变得阻塞了。
提到网络编程,就不得不提在实际项目中,如何与多线程进行配合。对于在多线程中直接操作API,方法比较多,具体的做法要看项目需求。这里我针对CSocket在多线程环境中可能出现的问题进行分析和讨论。
由于CSocket的网络调用是阻塞的,在复杂的网络环境中,一个网络操作函数返回的时间可能让用户无法接受,大家都能想到,需要用多线程来处理,你的想法很好,可是尝试过后,才知道不易啊。多线程中使用CSocket的主要现象是,你在一个线程里调用了CSocket的成员函数,之后启动一个线程,在另一线程中对CSocket对象调用成员函数会失败!通过DEBUG,可以发现一般是在下面这句发送诊断错误:
    _AFX_SOCK_THREAD_STATE* pState = _afxSockThreadState;
    ASSERT(pState->m_hSocketWindow != NULL);
_AFX_SOCK_THREAD_STATE我们不用太深入的了解它,先从感性上认识,这应该是个和线程状态相关的一个类。诊断时发生错误说明和SOCKET相关的窗口不存在。
MSDN里这样有这样的一句:CSocket /CAsyncSocket is not thread-safety ,为什么不是线程安全的呢?我主要考虑到的是消息循环是和窗口相关的,而这个窗口又是线程相关的。我的理解很肤浅,要深入理解的话,应该要看看MFC线程、模块切换的原理。
今天先写到这里。