在vs2008中编译通过,vs2010应该也没有问题。
使用静态MFC dll。
其他配置保持默认即可。
可能需要设置/MTD运行时。
可以实现向指定客户端ip发送信息
(该客户端必须首先连接进服务器)
从指定客户端ip中取出数据。
我使用C++类进行了封装,
对各种异常进行了处理。
进行了较详细的注释,
如果有函数不清楚,可以去msdn上查一查。
1,客户端类
#pragma once #define RECEIVE_SIZE 1024 /**************************************************** //Client function : save info of client ***************************************************/ class Client { public: Client(); ~Client(); public: CCriticalSection m_cs; BYTE m_pRemainBuf[RECEIVE_SIZE]; //数据缓存区 BYTE m_nDataLen; //数据缓冲区中已有数据的长度 BYTE m_pReceBuf[RECEIVE_SIZE]; //接受到的数据 OVERLAPPED m_OverLapped; WSABUF m_DataBuf; SOCKADDR_IN m_ClientAddress; //[!if NET_TYPE_TCP] SOCKET m_hClientSock; //communication socket; //[!endif] public: //***********将数据存入Client对应的缓冲区中******************* BOOL SetData(int len); //***********从Client对应的缓冲区中取出数据******************* int GetData(char * buff, int len); };
实现:
#include "stdafx.h" #include "Client.h" /**************************************************************************** * Name Client * Type public * Function construction function * Return Value null * Parameters null *****************************************************************************/ Client::Client() { //[!if NET_TYPE_TCP] m_hClientSock = INVALID_SOCKET; //[!endif] m_nDataLen = 0; memset(m_pRemainBuf, 0, RECEIVE_SIZE); memset(m_pReceBuf,0,RECEIVE_SIZE); memset(&m_OverLapped, 0, sizeof(m_OverLapped)); } /**************************************************************************** * Name ~Client * Type public * Function * Return Value null * Parameters null *****************************************************************************/ Client::~Client() { int a=0; a=3; } //*********************TCP接受数据************************ //**************缓冲区没有溢出,则将数据追加其后********** BOOL Client::SetData( int len ) { if (len<0) { return FALSE; } if (len==0) { return TRUE; } m_cs.Lock(); if (m_nDataLen+len>=RECEIVE_SIZE) { if (len<RECEIVE_SIZE) { memcpy_s(m_pRemainBuf, RECEIVE_SIZE, m_pReceBuf, len); m_nDataLen = len; } else { memcpy_s(m_pRemainBuf, RECEIVE_SIZE, m_pReceBuf, RECEIVE_SIZE); m_nDataLen = (BYTE)RECEIVE_SIZE; } } else { memcpy_s(m_pRemainBuf + m_nDataLen, RECEIVE_SIZE-m_nDataLen, m_pReceBuf, len); m_nDataLen += len; } m_cs.Unlock(); return TRUE; } int Client::GetData( char * buff, int len ) { if (!buff) { return 0; } int result=0; m_cs.Lock(); if (m_nDataLen <= len) { memcpy_s(buff, len, m_pRemainBuf, m_nDataLen); result = m_nDataLen; m_nDataLen = 0; } else { memcpy_s(buff, len, m_pRemainBuf, len); memmove_s(m_pRemainBuf, RECEIVE_SIZE, m_pRemainBuf + len, m_nDataLen-len); m_nDataLen -= len; result = len; } m_cs.Unlock(); return result; }
2,TCP完成端口的封装
头文件:
/************************************************************************/ //* 文件名称: IOCP_TCP.h //* 文件标示: //* 摘 要:采用完成端口技术处理网络数据 //* //* 当前版本:1.0 //* 作 者:hxy //* 完成日期:2013-11-16 //* /************************************************************************/ #ifndef _COMPLETIONPORT_H__ #define _COMPLETIONPORT_H__ #include <winsock2.h> #include <mswsock.h> #include "afxmt.h" #include <vector> #include "Client.h" using namespace std; #pragma once #define MAXTHREAD_COUNT 8 #define RECEIVE_SIZE 1024 #define PORT 502 class Client; class CompletionPort { public: CompletionPort(); ~CompletionPort(); //************初始化**************************** // 参数: 需要绑定的ip和port // 功能: 初始化socket库 // 设置需要绑定的ip和port //********************************************** BOOL Init(char * lpszHostAddress, UINT nHostPort); //************启动****************************** // 功能: 1,启动监听功能; 2,生成完成端口对象 //********************************************** BOOL Active(void); //************停止****************************** // 功能: 停止监听功能, // 释放Active中的申请的所有资源 //********************************************** BOOL Close(void); //************接受数据************************** // 功能: 接受指定客户端ip的数据 // 参数: buff:指向接受数据的缓冲区 // len: 缓冲区的大小 // ip: 客户端ip,如192.168.31.27 // 说明: buff的内存由调用者提供 //********************************************** int ReceiveData(char * buff, int len, char * ip); //************发送数据*************************** // 功能: 向指定的客户端ip的数据 // 参数: buff:指向发送数据的缓冲区 // len: 缓冲区的大小 // ip: 客户端ip,如192.168.31.27 //********************************************** int SendData(const char * buff, int len, char * ip); //-------------------------内部使用-------------------------------- //************监听******************************* // 功能: 监听客户端连接线程 //*********************************************** void MonitorServerSocket(); //************监听客户端发送的数据*************** // 功能: 监听客户端连接线程函数 //*********************************************** void MonitorIoCompletionPort(); //************处理客户端连接********************* // 功能: 客户端连接处理函数 //*********************************************** void OnClientAccept(); //************处理客户端关闭********************* // 功能: 客户端关闭处理函数 //*********************************************** BOOL OnClientClose(DWORD dwSockId); private: //************初始化Socket库********************* // 功能: 初始化Socket库 //*********************************************** BOOL InitWinsock(); void ProcessData(Client* pClient); Client* GetClient(DWORD dwSockId, int& index); //----------------------------Client 管理------------------------- //************增加客户端********************* BOOL AddClient( Client * const pClient); //************删除客户端********************* BOOL DeleteClient( int index); //************查询客户端********************* Client * GetClient( const char * ip); private: SOCKADDR_IN m_HostAddress; //服务器端地址(ip,port,协议)等 vector<Client*> m_ClientVector; //客户端表 rec_lock m_cs; //客户端表同步 BOOL m_bIsQuit; //退出标识 HANDLE m_hQuitEvent; //退出事件句柄 HANDLE m_hCOP; //完成端口句柄 HANDLE m_hThreadArray[MAXTHREAD_COUNT]; //接收数据线程句柄 int m_nThreadArray; //接收数据线程数量 HANDLE m_hListen; //监听连接线程句柄 SOCKET m_ListenSocket; //监听socket BOOL m_bQuitThread; //监听线程退出标识 WSAEVENT m_wsaEvent; //网络事件 }; #endif
实现文件:
#include "StdAfx.h" #include "IOCP_TCP.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /**************************************************************************** * Name ThreadTcpServer * Type public * Function thread to monitor a socket and accept a link * Return Value DWORD WINAPI * Parameters void * lpParameter *****************************************************************************/ DWORD WINAPI ThreadTcpServer(void * lpParameter) { CompletionPort* pCpPort = (CompletionPort*)lpParameter; pCpPort->MonitorServerSocket(); return 0; } /**************************************************************************** * Name CompletionRoutine * Type public * Function work thread of completionport * Return Value DWORD WINAPI * Parameters void * lpParameter translate a pointer of this *****************************************************************************/ DWORD WINAPI CompletionRoutine(void * lpParameter) { CompletionPort* pCpPort = (CompletionPort*)lpParameter; pCpPort->MonitorIoCompletionPort(); return 0; } /**************************************************************************** * Name CompletionPort * Type public * Function constructor * Return Value null * Parameters null *****************************************************************************/ CompletionPort::CompletionPort() { //_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF| _CRTDBG_LEAK_CHECK_DF); m_hCOP = INVALID_HANDLE_VALUE; m_hListen = INVALID_HANDLE_VALUE; for (int i=0; i< MAXTHREAD_COUNT; i++) { m_hThreadArray[i] = INVALID_HANDLE_VALUE; } m_bQuitThread = FALSE; m_wsaEvent = NULL; m_ListenSocket = INVALID_SOCKET; m_ClientVector.clear(); m_bIsQuit = FALSE; m_hQuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); m_nThreadArray = 0; } /**************************************************************************** * Name ~CompletionPort * Type public * Function do sth to release memory * Return Value null * Parameters null *****************************************************************************/ CompletionPort::~CompletionPort() { int i = 0; m_bIsQuit = TRUE; Sleep(10); if (INVALID_HANDLE_VALUE != m_hCOP) { m_bIsQuit = TRUE; //等待所有接受数据的线程结束 WaitForMultipleObjects(m_nThreadArray, m_hThreadArray, TRUE, INFINITE); while (INVALID_HANDLE_VALUE != m_hThreadArray[i]) { CloseHandle(m_hThreadArray[i]); m_hThreadArray[i-1] = INVALID_HANDLE_VALUE; i++; } //关闭完成端口 CloseHandle(m_hCOP); m_hCOP=NULL; } // 关闭监听socket if (INVALID_SOCKET != m_ListenSocket) { closesocket(m_ListenSocket); m_ListenSocket = NULL; } // 关闭监听线程 m_bQuitThread = TRUE; WaitForSingleObject(m_hListen, INFINITE); if (INVALID_HANDLE_VALUE != m_hListen) { CloseHandle(m_hListen); m_hListen = NULL; } WSACloseEvent(m_wsaEvent); m_wsaEvent = NULL; // 关闭客户端连接socket for( size_t i=0; i<m_ClientVector.size(); i++) { Client* pClient = m_ClientVector[i]; if( pClient != NULL) { if (pClient->m_hClientSock != INVALID_SOCKET) { shutdown(pClient->m_hClientSock, 2); closesocket(pClient->m_hClientSock); pClient->m_hClientSock = INVALID_SOCKET; } delete pClient; pClient = NULL; } } CloseHandle(m_hQuitEvent); m_ClientVector.clear(); } BOOL CompletionPort::Init(char * lpszHostAddress, UINT nHostPort) { if (!InitWinsock()) { return FALSE; } m_HostAddress.sin_family = AF_INET; m_HostAddress.sin_addr.s_addr = inet_addr((char *)lpszHostAddress); if (m_HostAddress.sin_addr.s_addr == INADDR_NONE) { LPHOSTENT lphost; lphost = gethostbyname((char *)lpszHostAddress); if (lphost != NULL) m_HostAddress.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr; else { WSASetLastError(WSAEINVAL); return FALSE; } } m_HostAddress.sin_port = htons((u_short)nHostPort); return true; } //************启动****************************** // 功能: 启动监听功能 //********************************************** BOOL CompletionPort::Active() { //-------------------------------监听连接--------------------------------------- //创建监听Socket m_ListenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED); if (INVALID_SOCKET == m_ListenSocket) { return FALSE; } int nResult = bind(m_ListenSocket, (PSOCKADDR)&m_HostAddress, sizeof(m_HostAddress)); if (SOCKET_ERROR == nResult) { closesocket(m_ListenSocket); return FALSE; } nResult = listen(m_ListenSocket, 20); if (SOCKET_ERROR == nResult) { closesocket(m_ListenSocket); return FALSE; } m_wsaEvent = WSACreateEvent(); if (NULL == m_wsaEvent) { return FALSE; } WSAEventSelect(m_ListenSocket, m_wsaEvent, FD_ACCEPT); //创建监听连接的线程 m_hListen = CreateThread(NULL, 0, ThreadTcpServer, (LPVOID)this, 0, NULL); if (NULL == m_hListen) { return FALSE; } //-------------------------------完成端口--------------------------------------- //创建完成端口句柄 m_hCOP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0); if (NULL == m_hCOP) { return FALSE; } //创建处理数据的线程 SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); for (int i=0; i<(int)SysInfo.dwNumberOfProcessors && (i<MAXTHREAD_COUNT); i++) { m_hThreadArray[i] = CreateThread(NULL, 0, CompletionRoutine, (LPVOID)this, 0, NULL); if (NULL == m_hThreadArray[i]) { while (i>0) { CloseHandle(m_hThreadArray[i-1]); m_hThreadArray[i-1] = INVALID_HANDLE_VALUE; i--; } return FALSE; } m_nThreadArray ++; } return TRUE; } //************从完成端口取数据****************** // 功能: 从完成端口取数据 // 返回: 字节数,如果ip未连接则返回0 //********************************************** int CompletionPort::ReceiveData(char * buff, int len, char * ip) { if (!buff || !ip) { return 0; } Client * pClient = GetClient(ip); if (!pClient) { return 0; } return pClient->GetData(buff, len); } //************发送数据*************************** // 功能: 向指定的客户端ip的数据 // 参数: buff:指向发送数据的缓冲区 // len: 要发送的数据的字节数 // ip: 客户端ip,如192.168.31.27 // 返回: 发送的字节数,错误则返回0 //********************************************** int CompletionPort::SendData(const char * buff, int len, char * ip) { if (!buff || !ip) { return 0; } int ret =0; //在取得客户端指针之后,完成端口线程会删除该客户端, //可能造成最null指针的解引用,所有这里使用结构化异常进行处理 __try { Client * pClient = GetClient(ip); if (!pClient) { return 0; } DWORD result = 0; ret =send(pClient->m_hClientSock, buff, len, 0); //int ret = WriteFile((HANDLE)pClient->m_hClientSock, buff, len, &result, NULL); } __except (EXCEPTION_EXECUTE_HANDLER) { printf("客户端已被删除,发送失败"); } return ret; } /**************************************************************************** * Name InitWinsock * Type public * Function initialize socket * Return Value BOOL * Parameters null *****************************************************************************/ BOOL CompletionPort::InitWinsock() { WSADATA wsd; int nResult = WSAStartup(MAKEWORD(2,2), &wsd); if (0 != nResult) { return FALSE; } return TRUE; } /**************************************************************************** * Name MonitorServerSocket * Type public * Function function to monitor a socket and accept a link * Return Value null * Parameters null *****************************************************************************/ void CompletionPort::MonitorServerSocket() { DWORD dwWaitCode; WSANETWORKEVENTS wsaNetWorkEvents; while( true ) { if( m_bQuitThread ) //判断监听线程退出条件是否满足 { SetEvent(m_hQuitEvent); TRACE("Monitor Server Quit\n"); return ; } dwWaitCode = WSAWaitForMultipleEvents(1, &m_wsaEvent, FALSE, 2000, FALSE);//等待网络事件 if(dwWaitCode != WAIT_OBJECT_0) continue; if( SOCKET_ERROR == WSAEnumNetworkEvents(m_ListenSocket, m_wsaEvent,&wsaNetWorkEvents ))//枚举网络事件 { int nErrorCode = WSAGetLastError(); TRACE("WSAEnumNetworkEvents Error"); } else { if( wsaNetWorkEvents.lNetworkEvents & FD_ACCEPT) { OnClientAccept(); } } } } /**************************************************************************** * Name OnClientAccept * Type public * Function function to monitor a socket and accept a link * Return Value null * Parameters null *****************************************************************************/ void CompletionPort::OnClientAccept() { int nSockAddrLength = sizeof(SOCKADDR_IN); Client* pClntData = new Client(); pClntData->m_hClientSock = accept(m_ListenSocket, (struct sockaddr*)&(pClntData->m_ClientAddress), &nSockAddrLength); if (INVALID_SOCKET == pClntData->m_hClientSock) { delete pClntData; } AddClient(pClntData); HANDLE hCompletePort = CreateIoCompletionPort((HANDLE)pClntData->m_hClientSock, m_hCOP, (DWORD)pClntData->m_hClientSock,0); if( hCompletePort != m_hCOP) { TRACE("Create Communication CompletionPort failure!\n"); } ReadFile((HANDLE)pClntData->m_hClientSock, pClntData->m_pReceBuf, RECEIVE_SIZE, NULL, &pClntData->m_OverLapped); } /**************************************************************************** * Name MonitorIoCompletionPort * Type public * Function function to monitor completionport * Return Value void * Parameters null *****************************************************************************/ void CompletionPort::MonitorIoCompletionPort() { TRACE("Begin Monitor IoPort \n"); BOOL bSuc = FALSE; DWORD dwNumBytes = 0; DWORD dwKey = -1; DWORD dwSockId = 0; LPOVERLAPPED completedOverlapped; int nLen =0; while( true ) { //判断监听线程退出条件是否满足 if( m_bIsQuit) { TRACE("Monitor IoCompletionPort Quit\n"); SetEvent(m_hQuitEvent); return; } //查询完全端口状态 dwKey = -1; bSuc = GetQueuedCompletionStatus( m_hCOP, &dwNumBytes, &dwKey, &completedOverlapped, 2000); if (bSuc==FALSE) { int nError = WSAGetLastError(); if (nError == WSA_WAIT_TIMEOUT) { continue; } if (nError == ERROR_NETNAME_DELETED) { OnClientClose(dwKey); continue; } TRACE("GetQueuedCompletionStatus Error: %d\n", nError); continue; } if (dwKey>0) { dwSockId = dwKey; nLen = dwNumBytes; if( nLen > 0) { int index = -1; Client* pClient = GetClient(dwSockId, index); if( pClient == NULL) continue; try { //Sleep(50); if (nLen >1024) { nLen = 1024; //CDebug::ShowErroMessage("接收到数据长度过长!"); } pClient->SetData(nLen); //ProcessData(pClient); //向完全端口发送读指令,等待下一次数据 ZeroMemory(pClient->m_pReceBuf,RECEIVE_SIZE); ReadFile((HANDLE)pClient->m_hClientSock,pClient->m_pReceBuf,RECEIVE_SIZE,NULL,&pClient->m_OverLapped); } catch(...) { TRACE("catch an error"); } } else { OnClientClose(dwSockId); } } } } /**************************************************************************** * Name GetClient * Type public * Function get a pointer to point class of Client * Return Value Client* * Parameters DWORD dwSockId : socket ID int& index: the position in the vector *****************************************************************************/ Client* CompletionPort::GetClient(DWORD dwSockId, int& index) { Client* pNewData = NULL; m_cs.lock(); for (size_t i = 0; i < m_ClientVector.size(); i++) { if (m_ClientVector[i]->m_hClientSock == dwSockId) { index = i; pNewData = m_ClientVector[i]; break; } } m_cs.unlock(); return pNewData; } /**************************************************************************** * Name OnClientClose * Type public * Function close the socket that dont translate data * Return Value BOOL * Parameters DWORD dwSockId: socket ID *****************************************************************************/ BOOL CompletionPort::OnClientClose(DWORD dwSockId) { int index = -1; Client* pClient = GetClient(dwSockId, index); if ((NULL != pClient) && (-1 != index)) { closesocket(pClient->m_hClientSock); DeleteClient(index); pClient = NULL; } return TRUE; } //----------------------------Client 管理------------------------- //************增加客户端********************* BOOL CompletionPort::AddClient(Client * const pClient) { if (!pClient) { return FALSE; } m_cs.lock(); m_ClientVector.push_back(pClient); m_cs.unlock(); return TRUE; } //************删除客户端********************* BOOL CompletionPort::DeleteClient(int index) { m_cs.lock(); delete m_ClientVector[index]; m_ClientVector.erase(m_ClientVector.begin() + index); m_cs.unlock(); return TRUE; } //************查询客户端********************* Client * CompletionPort::GetClient(const char * ip) { Client* pNewClient = NULL; ULONG nAddr = inet_addr(ip); m_cs.lock(); for (size_t i = 0; i < m_ClientVector.size(); i++) { if (m_ClientVector[i]->m_ClientAddress.sin_addr.s_addr == nAddr) { pNewClient = m_ClientVector[i]; break; } } m_cs.unlock(); return pNewClient; }
3,测试文件:
// IOCPTest.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "IOCP_TCP.h"
#include <iostream>
int _tmain(int argc, _TCHAR* argv[])
{
CCriticalSection *p = new CCriticalSection;
delete p;
CompletionPort * cp = new CompletionPort();
cp->Init("192.168.31.27", 4567); //需要修改为自己的ip
cp->Active();
char buff[1024]={0};
char recBuff[1024]={0};
int len;
while (true)
{
scanf_s("%s", buff, 1023);
len = strlen(buff);
cp->SendData(buff, len, "192.168.31.27"); //客户端的ip
ZeroMemory(recBuff,1024);
cp->ReceiveData(recBuff, 1024, "192.168.31.27"); //客户端的ip
printf("\n%s\n", recBuff);
}
delete cp;
return 0;
}
5,预编译头文件
// stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #define _WIN32_WINNT 0x0501 #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers #include <afxwin.h> // MFC core and standard components #include <afxext.h> // MFC extensions #ifndef _AFX_NO_OLE_SUPPORT #include <afxole.h> // MFC OLE classes #include <afxodlgs.h> // MFC OLE dialog classes #include <afxdisp.h> // MFC Automation classes #endif // _AFX_NO_OLE_SUPPORT #ifndef _AFX_NO_DB_SUPPORT #include <afxdb.h> // MFC ODBC database classes #endif // _AFX_NO_DB_SUPPORT #ifndef _AFX_NO_DAO_SUPPORT #include <afxdao.h> // MFC DAO database classes #endif // _AFX_NO_DAO_SUPPORT #include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls #ifndef _AFX_NO_AFXCMN_SUPPORT #include <afxcmn.h> // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT //外部库文件引用 #include <afxmt.h> #include <afx.h> #include <afxtempl.h> #include <winsock2.h> #include <memory> //外部自定义头文件引用 //#include "Resource.h" //#include "ICOMPro.h" //#include "datatype.h" //#include "Reg.h" //#include "Debug.h" //#include "Helper.h" #if defined(_WIN32_WCE_CEPC) || defined(_ARM_) #include "AtomCE.h" #endif //编译选项 //[!if TRYCONNECT_TRUE] //#define _TRYCONNECT_TRUE //[!endif] // //[!if TRYCONNECT_USERDEFINE] //#define _TRYCONNECT_USEDEFINE //[!endif] // //[!if TRYCONNECT_USEPACKET] //#define _TRYCONNECT_USEPACKET //[!endif] // //[!if REOPENCOM] //#define _REOPENCOM //[!endif] //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #include "lock.h"