C++套接字类CxUdpSocket的设计
这是一个小巧的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...