化繁为简系列原创教程

这是一个小巧的C++套接字类,类名、函数名和变量名均采用匈牙利命名法。小写的x代表我的姓氏首字母(谢欣能),个人习惯而已,如有雷同,纯属巧合。

CxUdpSocket的定义如下:

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

public:
    BOOL Bind(int nPort);
    BOOL Disbind();
    BOOL IsBinded();

    BOOL SendTo(LPCSTR lpszIPAddr, LPBYTE lpbtData, DWORD dwSize);
    BOOL RecvFrom(LPSTR lpszIPAddr, LPBYTE lpbtData, DWORD dwSize);

protected:
    int m_nPort;
};

由于这个类被封装在动态库里面,所以类名前使用了导出标志XIOCTRL_CLASS,读者在使用时完全可以去掉。类的定义被放在一个包含很多类定义的头文件中,没有单独为它写头文件,所以它的定义部分代码看上去没有上下文。

  CxUdpSocket的实现如下:

CxUdpSocket::CxUdpSocket()
: m_nPort(0)
{

}

CxUdpSocket::~CxUdpSocket()
{

}
    
BOOL CxUdpSocket::Bind(int nPort)
{
    Disbind();

    if (m_socket == INVALID_SOCKET)
        m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (m_socket == INVALID_SOCKET)
        return FALSE;

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(nPort);
    
    int iRet = bind(m_socket, (SOCKADDR*)&addr, sizeof(addr));
    if (iRet == SOCKET_ERROR)
    {
        Disbind();
        DWORD dwError = WSAGetLastError();
        return FALSE;
    }

    long lEvent = FD_WRITE | FD_READ | FD_CLOSE;
    SelectEvent(lEvent);
    m_nPort = nPort;

    return TRUE;
}

BOOL CxUdpSocket::IsBinded()
{
    sockaddr_in saCur = {0};
    int nLen = sizeof(saCur);
    int iResult = getsockname(m_socket, (SOCKADDR*)&saCur, &nLen);
    return (iResult != SOCKET_ERROR);
}

BOOL CxUdpSocket::SendTo(LPCSTR lpszIPAddr, LPBYTE lpbtData, DWORD dwSize)
{
    if (m_socket == INVALID_SOCKET)
        return FALSE;

    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr  = inet_addr(lpszIPAddr);
    addr.sin_port = htons(m_nPort);
    
    DWORD nMaxSize = MAX_MSG_SIZE, nCount = 0, nToSend;
    int iRet;
    LPBYTE lpbtIterator;
    
    while (nCount != dwSize)
    {
        nToSend = min((dwSize - nCount), nMaxSize);
        lpbtIterator = &lpbtData[nCount];
        iRet = sendto(m_socket, (const char*)lpbtIterator, nToSend, 0,
                        (SOCKADDR*)&addr, sizeof(addr));
        if (iRet > 0)
            nCount += iRet;
        else
            break;
    }

    return (nCount == dwSize);
}

BOOL CxUdpSocket::RecvFrom(LPSTR lpszIPAddr, LPBYTE lpbtData, DWORD dwSize)
{
    if (m_socket == INVALID_SOCKET)
        return FALSE;

    sockaddr_in addrRemote = {0};
    int nSize = sizeof(addrRemote);
    DWORD nMaxSize = MAX_MSG_SIZE;
    DWORD nCount = 0;
    DWORD nToReceive;
    int iRet;
    LPBYTE lpbtIterator;
    
    while (nCount != dwSize)
    {
        nToReceive = min((dwSize - nCount), nMaxSize);
        lpbtIterator = &lpbtData[nCount];
        iRet = recvfrom(m_socket, (char*)lpbtIterator, nToReceive, 0,
                        (SOCKADDR*)&addrRemote, &nSize);
        if (iRet > 0)
            nCount += iRet;
        else
            break;
    }
    
    strcpy(lpszIPAddr, inet_ntoa(addrRemote.sin_addr));
    return (nCount == dwSize);
}

BOOL CxUdpSocket::Disbind()
{
    if (m_socket == INVALID_SOCKET)
        return TRUE;

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

    m_socket = INVALID_SOCKET;
    m_nPort = 0;
    return TRUE;
}

类的实现被放在一个包含很多类实现的CPP文件中,没有单独为它写CPP文件,所以它的实现部分代码看上去没有上下文(比如头文件包含、宏定义等等)。MAX_MSG_SIZE是一个定义为1024的宏,来自对另一个头文件的引用(将来的文章会向大家介绍)。这个类的实现部分的代码不多,总共120多行。实现了(解)绑定地址与端口、发送接收数据以及侦听接收数据的功能(仅以消息响应的方式通知上层程序处理接收数据)。

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


你可能感兴趣的:(化繁为简系列原创教程)