基于select的I/O模型的封装,采用反弹模式,网络上有很多关于select封装,也与本文的类似,本文只是按照自己的习惯封装了一个类,方便使用.
// SrvSelect.h // By LengF 20130506 #include <string> using namespace std; #define SOCKET_TIMEOUT -100 class CSrvSelect { public: CSrvSelect(); virtual ~CSrvSelect(); public: SOCKET StartConnect(string szHost,UINT nPort); int SrvRecv(SOCKET s, char *buf, int len, int flag , int overtime,char*EndMark,BOOL soonflag=FALSE); int SrvSend(SOCKET s, const char *buf, int len, int flag,int overtime); void ErrorPrint(LPCTSTR lpOutputString ); public: SOCKET m_socket; BOOL m_bError; };
下面实现类函数和各种初始化:
// SrvSelect.cpp: implementation of the CSrvSelect class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "SrvSelect.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CSrvSelect::CSrvSelect() { // Init Socket Library WSADATA wsa; WSAStartup(MAKEWORD(2,2),&wsa); // Data Init m_socket = INVALID_SOCKET; m_bError = TRUE; // Debug Mode } CSrvSelect::~CSrvSelect() { if (m_socket != INVALID_SOCKET) { closesocket(m_socket); shutdown(m_socket,2); m_socket = INVALID_SOCKET; } WSACleanup(); } void CSrvSelect::ErrorPrint(LPCTSTR lpOutputString) { if (m_bError) { printf("%s\n",lpOutputString); //OutputDebugString(lpOutputString); } } SOCKET CSrvSelect::StartConnect(string szHost, UINT nPort) { SOCKET s = INVALID_SOCKET; SOCKADDR_IN ClientAddr; LPHOSTENT Host; // for DNS memset(&ClientAddr,0,sizeof(SOCKADDR_IN)); if (szHost.empty() || nPort ==0) // Params Error return INVALID_SOCKET; Host=gethostbyname(szHost.c_str()); ClientAddr.sin_family = AF_INET; ClientAddr.sin_port = htons((u_short)nPort); // Host Port ClientAddr.sin_addr = *((LPIN_ADDR)*Host->h_addr_list); // Host IP // create socket // PF_INET for IPV6,IPV4 s = socket(PF_INET,SOCK_STREAM,0); if(connect(s,(LPSOCKADDR)&ClientAddr,sizeof(ClientAddr)) == SOCKET_ERROR) // connect error { ErrorPrint("[s] Connect Error."); closesocket(s); shutdown(s,2); s = INVALID_SOCKET; } m_socket = s; return s; } int CSrvSelect::SrvSend(SOCKET s, const char *buf, int len, int flag, int overtime) { int ret; int nLeft = len; int idx = 0; fd_set readfds; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 500; DWORD s_time = GetTickCount(); while ( nLeft > 0 ) { MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ; if(msg.message == WM_QUIT) return 0; FD_ZERO( &readfds ); FD_SET( s , &readfds ); int errorret = select( 0 , NULL, &readfds, NULL , &timeout ); if( errorret == SOCKET_ERROR ) { ErrorPrint("[s] Socket select error."); return SOCKET_ERROR; } DWORD e_time = GetTickCount( ); if ( !FD_ISSET( s , &readfds ) ) { if( e_time - s_time > overtime*1000 ) { ErrorPrint("[s] Send Data TimeOut."); return 0; } else { continue; } } ret = send( s, &buf[idx], nLeft, flag ); if ( ret <= 0 ) { return ret; } nLeft -= ret; idx += ret; } // end while return len; } int CSrvSelect::SrvRecv(SOCKET s, char *buf, int len, int flag, int overtime, char *EndMark, BOOL soonflag) { int ret; int nLeft = len; int idx = 0; int nCount = 0; fd_set readfds; struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 500; DWORD s_time = GetTickCount(); while ( nLeft > 0 ) { MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ; if(msg.message == WM_QUIT) return 0; FD_ZERO( &readfds ); FD_SET( s , &readfds ); if( select( 0 , &readfds , NULL , NULL , &timeout ) == SOCKET_ERROR ) { return SOCKET_ERROR; } DWORD e_time = GetTickCount( ); if ( !FD_ISSET( s , &readfds ) ) { if( e_time - s_time > overtime*1000 ) return SOCKET_TIMEOUT; else continue; } ret = recv( s, &buf[idx], nLeft, flag ); if( soonflag == TRUE ) { return ret; } s_time = e_time ; // reset time if ( ret <= 0 ) { int LastError = GetLastError(); if ( ( -1 == ret ) && ( WSAETIMEDOUT == LastError ) ) continue; if ( ( -1 == ret ) && ( WSAEWOULDBLOCK == LastError ) ) { if ( nCount < 2000 ) { Sleep( 10 ); nCount++; continue; } } return ret; } nCount = 0; nLeft -= ret; idx += ret; if( EndMark != NULL && idx>5) { if( strstr(buf+(idx-5),EndMark) != NULL ) { break; } } }// end while return idx; }
至此整个类的封装已经完成了.如果有什么想法,或者BUGS欢迎交流,同时我也会在平时的应用中及时更新发现的问题.