[WinSock]封装WSAAsyncSelect!

[接上篇]

封装目标: 最终目标是封装WinSock的WSAAsyncSelect IO模型。

封装原则: 耦合性[减少各种依赖,包括classes之间,编译模块之间。],小粒度增加可复用性。

依赖ATL/WTL。 


CKxAsyncSocket 是我要实现的class,它只需要维护SOCKET成员,另外因为是异步选择,所以可以再维护一个HWND。

关于HWND,是否有必要暴露给用户,关键在于Socket要仅仅运行于UI主线程,还是用户可以自己创建另一个有消息循环的Thread,在其上处理Socket的消息。

我选择后者,因为,尽管异步的Socket,在Recv大数据的时候,还是可能导致UI“卡”的。

 

那么,我们就需要一个线程,一个有处理消息的窗口,二者形成一个UI线程, 然后再该UI线程处理Socket消息。以上提到的几个实体,实体之间要尽量低耦合,无依赖。

【通常来说,接口耦合和可以接受的,我们也会通过一些技巧来实现低耦合】

1. 线程只需要一个简单的CreateThread封装,Like Java Style的就好,支持Runnable接口,好处就不说了。

2. 处理消息的窗口,我们用WTL,搞个简单的:

#define CHAIN_MSG_MAP_MEMBER_PTR(theChainMemberPtr) \
    { \
         if( NULL != theChainMemberPtr && \
            theChainMemberPtr->ProcessWindowMessage(hWnd, uMsg, wParam, lParam, lResult)) \
             return TRUE; \
    }
    
class MessageOnlyWindow :  public CWindowImpl<MessageOnlyWindow, CWindow, CWinTraits<> >
{
     public:
        BEGIN_MSG_MAP(MessageOnlyWindow)
            CHAIN_MSG_MAP_MEMBER_PTR( m_pWinMsgHandler )
        END_MSG_MAP()

        MessageOnlyWindow(){}

        ~MessageOnlyWindow()
        {
            ::DestroyWindow(m_hWnd);
            m_hWnd = NULL;
        }

        HWND Create(ProcessWindowMessageHandler* pWinMsgHandler)
        {
            m_pWinMsgHandler = pWinMsgHandler;
             return CWindowImpl<MessageOnlyWindow, CWindow, CWinTraits<> >::Create(HWND_MESSAGE);
        }
     private:
        ProcessWindowMessageHandler* m_pWinMsgHandler;

};  

需要说明一下:

CHAIN_MSG_MAP_MEMBER_PTR是仿WTL的宏,WTL缺少一个处理成员指针的CHAIN*. 这样
HWND_MESSAGE窗口的消息,都可以投递给
ProcessWindowMessageHandler* m_pWinMsgHandler了。

 

PS:

class __declspec(novtable) ProcessWindowMessageHandler
{
public:
     virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID =  0) =  0;
};
//  ProcessWindowMessage和BEGIN_MSG_MAP的声明是一致的,但是处理成虚函数了。这样可以多态到派生类,但是又不会依赖派生类(的声明)
// WTL大量依赖模板来处理这种关系。 

那么现在,我们再创建一个类,表达UI Thread的概念即可,它可以继承或者聚合一个CKxThread即可。然后在线程中创建窗口和消息循环即可:

virtual  int Run( CKxThread* pThread ) 

{
    MessageOnlyWindow msgWnd;
    HWND hWnd = msgWnd.Create(m_pRoutine);

    m_pRoutine->OnBegin( pThread, hWnd );

    MSG msg;
    while( GetMessage(&msg, NULL, 00) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    return 0;
} // 代码有节略.

再看下m_pRoutine的类型:

 

class __declspec(novtable) IKxMessageRoutine :  public ProcessWindowMessageHandler

 

实际上用户自己派生的

IKxMessageRoutine ,就可以通过Windows Message Thread实例传递给
MessageOnlyWindow。这样用户的派生类就可以直接Handle这个Thread的消息了,看一下这个派生类的样子:

 

class CUserThreadMessageHandler :  public IKxMessageRoutine,  public CKxAsyncSocketMessageMixin
{
private:
    BEGIN_MSG_MAP(
CUserThreadMessageHandler)
        CHAIN_MSG_MAP(CKxAsyncSocketMessageMixin)

    END_MSG_MAP()  

首先,我们可以确定的是,

CUserThreadMessageHandler仅仅是消息的处理者,而且和Socket事件没有必然联系,如果不继承
CKxAsyncSocketMessageMixin(忽略此处的Public继承)的话。因为Windows中,除了异步选择的Socket IO模型,还有很多依赖消息循环的编程模型。所以不考虑第二个继承关系的
CUserThreadMessageHandler是一个很好的处理UI 线程消息的对象。在该线程中,需要处理的消息,可以按照WTL的消息映射写处理函数。

 

那么,我们现在要处理Socket的事件了,好,加一个Mixin即可【当然了,要通过

CHAIN_MSG_MAP来把CKxAsyncSocketMessageMixin接入到消息队列中】,这样,一个Socket绑定了这个线程的消息窗口后,就可以在这个线程的消息循环中工作了。

 


CKxAsyncSocketMessageMixin是一个处理Socket事件的类,并且把SOCKET<->Socket对象指针进行映射【没办法,OS投递SOcket事件的时候,参数是SOCKET,而不是别的,所以自己要做这个Mapping,搞过WinSock封装的人,都清楚这点。HWND<->CWnd*的映射也是一般道理。】

class CKxAsyncSocketMessageMixin 

{
public:
    
    BEGIN_MSG_MAP(CKxAsyncSocketMessageMixin)
        MESSAGE_HANDLER(HK_WM_SOCKET, OnSocketMessage)
... ...

// HK_WM_SOCKET就是WinSock API WSAAsyncSelect的第三个参数,你懂的。 

最后,Socket对象也就可以收到消息了。然后根据消息转到不同的处理函数即可,OnConnect,OnRecv...[并且我的Socket,也实行了ProcessWindowMessageHandler接口]


-------------------------------------------------------------------------------------------------------------------------------------------------------

// Socket 本身相关的. 

[WinSock]其实和WTL关系不大,但是我会用到一些WTL的东西。

 

因为是异步选择,所以需要一个窗口来接收消息,于是...

class  MessageOnlyWindow :  public  CWindowImpl < MessageOnlyWindow, CWindow, CWinTraits <>  >
{
    BEGIN_MSG_MAP(MessageOnlyWindow)
    MESSAGE_HANDLER(WM_SOCKETOnSocketMessage)
    END_MSG_MAP()
 

    HWND Create()

    {
return CWindowImpl<MessageOnlyWindow, CWindow, CWinTraits<> >::Create( HWND_MESSAGE);

 } 

};

这样,非常简单的搞定了消息窗口【可算是和WTL扯上关系了】


异步Socket的Receive才是我关注的:

[1]       

WSAAsyncSelect自动把一个阻塞的socket转为非阻塞的,如果需要转为阻塞的,那么先要调用这个函数,并且(最后一个参数long lEvent设置为0) 。然后调用ioctlsocket,或者WSAIoctl。 

// MSDN:The WSAAsyncSelect function automatically sets socket s to nonblocking mode, regardless of the value of lEvent. To set socket s back to blocking mode, it is first necessary to clear the event record associated with socket s via a call to WSAAsyncSelect with lEvent set to zero. You can then call ioctlsocket or WSAIoctl to set the socket back to blocking mode.

设置为阻塞的:

 

    u_long iMode =  0;     //  set blocking.
    ioctlsocket( m_hSocket, FIONBIO, &iMode );

 


[2] 

一旦设置了阻塞,我想,还是加个超时的好:

 

    DWORD dwRecvTimeout =  5000//  5 sec timeout;
    setsockopt( m_hSocket, SOL_SOCKET, SO_RCVTIMEO, ( char*)&dwRecvTimeout,  sizeof(DWORD) );

 

 

转载于:https://www.cnblogs.com/healerkx/archive/2011/09/24/2189526.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在WINDOWS下进行网络服务端程序开发,毫无疑问,Winsock 完成端口模型是最高效的。Winsock的完成端口模型借助Widnows的重叠IO和完成端口来实现,完成端口模型懂了之后是比较简单的,但是要想掌握Winsock完成端口模型,需要对WINDOWS下的线程、线程同步,Winsock API以及WINDOWS IO机制有一定的了解。如果不了解,推荐几本书:《Inside Windows 2000,《WINDOWS核心编程》,《WIN32多线程程序设计》、《WINDOWS网络编程技术》。在去年,我在C语言下用完成端口模型写了一个WEBSERVER,前些天,我决定用C++重写这个WEBSERVER,给这个WEBSERVER增加了一些功能,并改进完成端口操作方法,比如采用AcceptEx来代替accept和使用LOOKASIDE LIST来管理内存,使得WEBSERVER的性能有了比较大的提高。 (under the Windows network server program development, no doubt, Winsock completed port model is the most efficient. Winsock port model with complete preview of overlap and complete port IO to achieve complete port after the model to understand is a relatively simple, but want to know the complete Winsock port model, the need for the Windows threads, thread synchronization, Winsock API and Windows IO mechanism of a certain understanding. If we do not understand, recommended several books : "Inside Windows 2000," Windows core programming "," WIN32 multithreaded programming "," WINDOWS network programming technology. " Last year, I used C language under complete port model was a WEBSERVER, few days ago, I decided to rewrite the C WEBSERVER to the WEBSERVER i)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值