这是一个小巧的客户端套接字类,类名、函数名和变量名均采用匈牙利命名法。小写的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, this, 0, &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 0x%X(CxClientSocket::ListenProc) has entered.\n", dwID); OutputDebugString(szDebug); CxClientSocket* pThis = (CxClientSocket*)lpParam; if (pThis != NULL) pThis->OnListen(); sprintf(szDebug, "The thread 0x%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 = select((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...