化繁为简系列原创教程 - 通信专题 - 客户端套接字类CxClientSocket的设计

    添加时间:2013-8-5 点击量:

      这是一个小巧的客户端套接字类,类名、函数名和变量名均采取匈牙利定名法。小写的x代表我的姓氏首字母(谢欣能),小我习惯罢了,如有类似,纯属偶合。


    CxClientSocket的定义如下:



    class XIOCTRL_CLASS CxClientSocket : public CxSocket
    
    {
    public:
    CxClientSocket();
    virtual ~CxClientSocket();
    void operator=(SOCKET s) { m_socket = s; }

    public:
    virtual BOOL Connect(LPCSTR lpszIPAddr, int nPort);
    virtual BOOL IsConnected();
    BOOL DisConnect();

    BOOL Send(LPBYTE lpbtData, DWORD dwSize);
    BOOL Recv(LPBYTE lpbtData, DWORD dwSize);
    BOOL GetRemoteHost(LPSTR lpszIPAddr);

    protected:
    BOOL Listen();
    void DisListen();
    static DWORD WINAPI ListenProc(LPVOID lpParam);
    void OnListen();

    protected:
    HANDLE m_hThreadListen;
    BOOL m_bExitThreadListen;
    };


      因为这个类被封装在动态库里面,所以类名前应用了导出标记XIOCTRL_CLASS,读者在应用时完全可以去掉。这个类的定义被放在一个包含很多类定义的头文件中,我没有零丁为它写头文件,所以它的定义项目组代码看上去没有高低文。


    CxClientSocket的实现如下:



    CxClientSocket::CxClientSocket()
    
    : m_hThreadListen(NULL)
    , m_bExitThreadListen(TRUE)
    {

    }

    CxClientSocket::
    ~CxClientSocket()
    {

    }

    BOOL CxClientSocket::GetRemoteHost(LPSTR lpszIPAddr)
    {
    sockaddr_in sa
    = {0};
    int nSize = sizeof(sa);
    int iResult = getpeername(m_socket, (sockaddr)&sa, &nSize);
    if (iResult == SOCKET_ERROR)
    return FALSE;
    else
    {
    strcpy(lpszIPAddr, inet_ntoa(sa.sin_addr));
    return TRUE;
    }
    }

    BOOL CxClientSocket::Listen()
    {
    if (m_hThreadListen != NULL)
    return FALSE;

    m_bExitThreadListen
    = FALSE;
    DWORD dwThreadID;
    m_hThreadListen
    = CreateThread(NULL, 0, ListenProc, this0, &dwThreadID);

    return (m_hThreadListen != NULL);
    }

    void CxClientSocket::DisListen()
    {
    if (m_hThreadListen != NULL)
    {
    m_bExitThreadListen
    = TRUE;

    WaitForSingleObject(m_hThreadListen, INFINITE);
    CloseHandle(m_hThreadListen);
    m_hThreadListen
    = NULL;
    }
    }

    DWORD WINAPI CxClientSocket::ListenProc(LPVOID lpParam)
    {
    DWORD dwID
    = ::GetCurrentThreadId();
    char szDebug[MAX_PATH];
    sprintf(szDebug,
    The thread 0 x%X(CxClientSocket::ListenProc) has entered.\n, dwID);
    OutputDebugString(szDebug);

    CxClientSocket
    pThis = (CxClientSocket)lpParam;
    if (pThis != NULL)
    pThis
    ->OnListen();

    sprintf(szDebug,
    The thread 0 x%X(CxClientSocket::ListenProc) has exited.\n, dwID);
    OutputDebugString(szDebug);

    return 0;
    }

    void CxClientSocket::OnListen()
    {
    fd_set fd;
    FD_ZERO(
    &fd);
    FD_SET(m_socket,
    &fd);

    /struct timeval timet;
    timet.tv_sec = 1;
    timet.tv_usec = 0;
    /

    while (!m_bExitThreadListen)
    {
    int i = ((int)m_socket, &fd, NULL, NULL, NULL);
    if (i > 0
    {
    if (m_wndproc != NULL)
    {
    m_wndproc(m_msg.hwnd, m_msg.message,
    (WPARAM)m_socket, (LPARAM)MAKELPARAM(FD_READ,
    0));
    }
    }
    }
    }

    BOOL CxClientSocket::Connect(LPCSTR lpszIPAddr,
    int nPort)
    {
    DisConnect();

    if (m_socket == INVALID_SOCKET)
    m_socket
    = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET)
    return FALSE;

    sockaddr_in service
    = {0};
    service.sin_family
    = AF_INET;
    service.sin_addr.s_addr
    = inet_addr(lpszIPAddr);
    service.sin_port
    = htons(nPort);

    int iResult = connect(m_socket, (SOCKADDR)&service, sizeof(service));
    if (iResult == SOCKET_ERROR)
    {
    DisConnect();
    DWORD dwError
    = WSAGetLastError();
    return FALSE;
    }

    long lEvent = FD_WRITE | FD_CLOSE;
    if (m_wndproc == NULL)
    lEvent
    |= FD_READ;
    else
    Listen();

    SelectEvent(lEvent);

    return TRUE;
    }

    BOOL CxClientSocket::DisConnect()
    {
    if (m_socket == INVALID_SOCKET)
    return TRUE;

    if (IsConnected())
    {
    int iResult = shutdown(m_socket, SD_BOTH);
    if (iResult == SOCKET_ERROR)
    return FALSE;
    }

    DisListen();

    int iResult = closesocket(m_socket);
    if (iResult == SOCKET_ERROR)
    return FALSE;

    m_socket
    = INVALID_SOCKET;
    return TRUE;
    }

    BOOL CxClientSocket::IsConnected()
    {
    sockaddr_in saCur
    = {0};
    int nLen = sizeof(saCur);
    int iResult = getpeername(m_socket, (SOCKADDR)&saCur, &nLen);
    return (iResult != SOCKET_ERROR);
    }

    BOOL CxClientSocket::Send(LPBYTE lpbtData, DWORD dwSize)
    {
    DWORD nCount
    = 0, nToSend;
    int iRet;
    LPBYTE lpbtIterator;
    DWORD dwError;

    while (nCount != dwSize)
    {
    nToSend
    = dwSize - nCount;
    lpbtIterator
    = &lpbtData[nCount];
    iRet
    = send(m_socket, (const char)lpbtIterator, nToSend, 0);
    if (iRet != SOCKET_ERROR)
    nCount
    += iRet;
    else // something wrong
    {
    dwError
    = WSAGetLastError();
    if (dwError != WSAEWOULDBLOCK)
    break;
    }
    }

    return (nCount == dwSize);
    }

    BOOL CxClientSocket::Recv(LPBYTE lpbtData, DWORD dwSize)
    {
    DWORD nCount
    = 0;
    int nToReceive, iRet;
    LPBYTE lpbtIterator;
    DWORD dwError;

    while (nCount != dwSize)
    {
    nToReceive
    = dwSize - nCount;
    lpbtIterator
    = &lpbtData[nCount];
    iRet
    = recv(m_socket, (char)lpbtIterator, nToReceive, 0);
    if (iRet != SOCKET_ERROR)
    {
    if (iRet == 0// disconnect by remote host
    {
    //dwError = WSAGetLastError(); // No error
    WSASetLastError(WSAENOTCONN);
    break;
    }
    else
    nCount
    += iRet;
    }
    else // something wrong
    {
    dwError
    = WSAGetLastError();
    if (dwError != WSAEWOULDBLOCK)
    break;
    }
    }

    return (nCount == dwSize);
    }


      这个类的实现被放在一个包含很多类实现的CPP文件中,没有零丁为它写CPP文件,所以它的实现项目组代码看上去没有高低文(比如头文件包含、宏定义等等)。这个类的实现项目组的代码不久不多,统共210多行。实现了(断开)连接办事端、发送接管数据以及侦听接管缓存区数据的功能(以消息响应或回调函数的体式格式通知上层法度处理惩罚接管缓存区数据)。


      我写的很多实用类都很是简洁,一般都没有注释,有也是中英文混搭两句,大师习惯就好。To be continued...

    文艺不是炫耀,不是花哨空洞的文字堆砌,不是一张又一张的逆光照片,不是将旅行的意义转化为名牌包和明信片的物质展示;很多时候它甚至完全不美——它嘶吼、扭曲,它会痛苦地抽搐,它常常无言地沉默。——艾小柯《文艺是一种信仰》
    分享到: