参考:《windows网络编程》、度娘、谷歌、还有一篇博客和它的源码http://blog.csdn.net/piggyxp/article/details/6922277,感谢PiggyXP,不过我的代码有更好的可读性,感谢broadcom的嵌入式C++系统带给我的编程经验,不过流程基本相似,可以关注一些细节
HttpServer\targetver.h:
Windows platform header,系统生成的
HttpServer\stdafx.h:
系统头文件和一些通用define
HttpServer\stdafx.cpp :
预编译
stdafx.h使用的源文件,无内容
HttpServer\Start.cpp:
console程序入口,目前不必实现stop,因为结束console主线程,该进程的其余线程也将退出
TODO:做界面的时候再实现stop和restart功能,可能需要使用PostQueuedCompletionStatus函数来通知worker线程退出
HttpServer\IOCPServer.h:
HttpServer\IOCPServer.cpp:
完成端口模型的TCP server
HttpServer\SocketHandle.h:
HttpServer\SocketHandle.cpp:
对应于每一个socket的handle类定义,一个socket handle可持有多个IO数据组成的list
HttpServer\PerIOData.h:
HttpServer\PerIOData.cpp:
单个IO数据,包含
overlapped结构,而且该结构必须在类内存空间的最开始
流程图:
源码如下,其他的不细说了,看注释吧,不过是英文的,英文语法就别深究了:
HttpServer\stdafx.h:
- // stdafx.h : include file for standard system include files,
- // or project specific include files that are used frequently, but
- // are changed infrequently
- //
- #pragma once
- #include "targetver.h"
- #include <cstdio>
- #include <cassert>
- #include <tchar.h>
- #include <string.h>
- // TODO: reference additional headers your program requires here
- // win header and lib
- #include <winsock2.h>
- #include <MSWSock.h>
- #pragma comment(lib,"ws2_32.lib")
- #include <list>
- using namespace std;
- #define IOCP_DBUG(format, ...) printf("%s,%d: " format, __FUNCTION__, __LINE__, ##__VA_ARGS__);
HttpServer\Start.cpp
- #include "stdafx.h"
- #include "IOCPServer.h"
- int _tmain(int argc, _TCHAR* argv[])
- {
- IOCP_DBUG("enter Start\n");
- IOCPServer *iocpserver = new IOCPServer();
- iocpserver->Start();
- while(TRUE)
- {
- Sleep(5000);
- }
- delete iocpserver;
- return 0;
- }
HttpServer\IOCPServer.h:
- #pragma once
- #include "stdafx.h"
- #include "PerIOData.h"
- #include "SocketHandle.h"
- // this is IOCPServer which use IOCP
- // has no GUI, we provide two public function to call
- // two public function: start() and stop()
- // you can write new interface
- class IOCPServer
- {
- public:
- IOCPServer(void);
- virtual ~IOCPServer(void);
- public:
- bool Start(void);
- bool Stop(void);
- // self member hold in whole life of the class object
- //so we have no need to tranfer these in function parameters
- private:
- HANDLE completeport;
- SocketHandle *listenhandle;
- private:
- LPFN_ACCEPTEX lpfnacceptex;
- LPFN_GETACCEPTEXSOCKADDRS lpfngetacceptexsockaddrs;
- //Initialize functions, check the cpp file
- //Return value for BOOL function:
- // true, if succeed;
- // false, if failed.
- //
- private:
- BOOL InitializeIOCP(void);
- void DeInitializeIOCP(void);
- BOOL InitializeSocket(void);
- void DeInitializeSocket(void);
- BOOL GetAcceptExFuction(void);
- private:
- // CreateThread only can use static function in class
- // so must transfer the 'this' pointer to lpParam to use non-static function member
- static DWORD WINAPI WorkerThread(LPVOID lpParam);
- // the six function member below has same parameter format and return value
- //
- //Parameter:
- // connecthandle - the client socket and addr storage struct for the operation
- // listeniodata/connectiodata - the io data for the operation
- // this struct PerIOData MUST begin with WSAOVERLAPPED/OVERLAPPED struct,
- // To convert the PerIOData pointer to OVERLAPPED pointer.
- // actually, the first address should be the same, so they are compatible
- //
- //Return value:
- // true, if succeed;
- // false, if failed.
- //
- private:
- // post function for AcceptEx
- // no need for do function, ACCEPT_POSTED signal corresponding to listen socket
- // listeniodata is hold in whole life of master thread
- // MUST not release in worker thread
- void PostAcceptEx(PerIOData *listeniodata);
- BOOL DoAcceptEx(PerIOData *listeniodata);
- private:
- // post and do function for recv/send
- // connectiodata is hold in worker thread
- void PostRecv(SocketHandle *connecthandle, PerIOData *connectiodata);
- BOOL DoRecv(SocketHandle *connecthandle, PerIOData *connectiodata);
- void PostSend(SocketHandle *connecthandle, PerIOData *connectiodata);
- BOOL DoSend(SocketHandle *connecthandle, PerIOData *connectiodata);
- };
HttpServer\IOCPServer.cpp:
- #include "stdafx.h"
- #include "IOCPServer.h"
- // we only post accpetex after do acceptex, if there is no mistake
- // so the MAX_POST_ACCEPT is the number we post accpetex first
- // and it is max client number we can accpet in the same time
- // but we almost can not see difference between 10 and 100 clients, because this program is simple and fast
- // In test, 100 clients seems to accept immediately in the same time, so this number have no need to be bigger
- #define MAX_POST_ACCEPT 10
- //the port
- #define SERVERPORT 5863
- //backlog, see Stevens' book, the nginx set this to 511
- //we have no need to set it to be that big
- #define BACKLOG 200
- //constructor
- IOCPServer::IOCPServer(void):
- completeport(NULL),
- listenhandle(NULL),
- lpfnacceptex(NULL),
- lpfngetacceptexsockaddrs(NULL)
- {
- IOCP_DBUG("enter IOCPServer\n");
- //load the win socket lib
- WSADATA wsaData;
- if(NO_ERROR != WSAStartup(MAKEWORD(2,2), &wsaData))
- {
- IOCP_DBUG("WSAStartup WinSock 2.2 failed!\n");
- assert(0);
- }
- //establish listen socket
- listenhandle = new SocketHandle();
- }
- // destructor
- IOCPServer::~IOCPServer(void)
- {
- IOCP_DBUG("enter ~IOCPServer\n");
- delete listenhandle;
- }
- // start the server
- // we use IOCP instead of select events
- // of cource, you can register other method, not implement here
- bool IOCPServer::Start(void)
- {
- IOCP_DBUG("enter Start\n");
- //Initialize IOCP
- if(FALSE == InitializeIOCP())
- {
- IOCP_DBUG("InitializeIOCP() failed: %d\n", GetLastError());
- DeInitializeIOCP();
- return FALSE;
- }
- IOCP_DBUG("InitializeIOCP() succeed\n");
- //Initialize Socket, associate it with IOCP
- if(FALSE == InitializeSocket())
- {
- IOCP_DBUG("InitializeSocket() failed: %d\n", GetLastError());
- DeInitializeSocket();
- return FALSE;
- }
- IOCP_DBUG("InitializeSocket() succeed\n");
- // get AcceptEx func pointer
- if(FALSE == GetAcceptExFuction())
- {
- IOCP_DBUG("GetAcceptExFuction() failed: %d\n", GetLastError());
- return FALSE;
- }
- IOCP_DBUG("GetAcceptExFuction() succeed\n");
- // new listeniodata only here
- // post some ACCEPT_POSTED signal, let the server start
- for(int i=0; i<MAX_POST_ACCEPT; ++i)
- {
- PerIOData *listeniodata = listenhandle->GetNewIOData();
- PostAcceptEx(listeniodata);
- }
- return TRUE;
- }
- //Initialize IOCP
- BOOL IOCPServer::InitializeIOCP(void)
- {
- IOCP_DBUG("enter InitializeIOCP\n");
- // establish completeport
- completeport = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0 );
- if ( NULL == completeport)
- {
- IOCP_DBUG("CreateIoCompletionPort failed: %d!\n", WSAGetLastError());
- return FALSE;
- }
- // run worker thread
- SYSTEM_INFO systeminfo;
- GetSystemInfo(&systeminfo);
- for(DWORD i=0; i < systeminfo.dwNumberOfProcessors; ++i)
- {
- HANDLE threadhandle;
- threadhandle = CreateThread(NULL, 0, WorkerThread, (LPVOID)this, 0, NULL);
- CloseHandle(threadhandle);
- }
- return TRUE;
- }
- //DeInitialize IOCP
- void IOCPServer::DeInitializeIOCP(void)
- {
- IOCP_DBUG("enter DeInitializeIOCP\n");
- CloseHandle(completeport);
- }
- //Initialize Socket
- BOOL IOCPServer::InitializeSocket(void)
- {
- IOCP_DBUG("enter InitializeSocket\n");
- listenhandle->ssocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
- if(INVALID_SOCKET == listenhandle->ssocket)
- {
- IOCP_DBUG("WSASocket failed: %d.\n", WSAGetLastError());
- return FALSE;
- }
- //associate listen socket with IOCP, let IOCP get ACCEPT_POSTED signal
- if (NULL == CreateIoCompletionPort((HANDLE)listenhandle->ssocket, completeport, (ULONG_PTR)listenhandle, 0))
- {
- IOCP_DBUG("CreateIoCompletionPort() failed: %d.\n", WSAGetLastError());
- return FALSE;
- }
- // set reuse addr option
- int reuseaddr = 1;
- if (SOCKET_ERROR ==
- setsockopt(listenhandle->ssocket, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuseaddr, sizeof(int)))
- {
- IOCP_DBUG("listenhandle->ssocket: setsockopt(SO_REUSEADDR) failed %d\n", WSAGetLastError());
- closesocket(listenhandle->ssocket);
- return FALSE;
- }
- //bind and listen
- SOCKADDR_IN ServerAddress;
- ZeroMemory((char *)&ServerAddress, sizeof(ServerAddress));
- ServerAddress.sin_family = AF_INET;
- ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY);
- ServerAddress.sin_port = htons(SERVERPORT);
- if (SOCKET_ERROR ==
- bind(listenhandle->ssocket, (PSOCKADDR) &ServerAddress, sizeof(SOCKADDR_IN)))
- {
- IOCP_DBUG("listensocket: bind() failed, %d\n", WSAGetLastError());
- closesocket(listenhandle->ssocket);
- return FALSE;
- }
- if (SOCKET_ERROR == listen(listenhandle->ssocket, BACKLOG))
- {
- IOCP_DBUG("listensocket: listen() failed %d\n", WSAGetLastError());
- closesocket(listenhandle->ssocket);
- return FALSE;
- }
- return TRUE;
- }
- //DeInitialize Socket
- void IOCPServer::DeInitializeSocket(void)
- {
- IOCP_DBUG("enter DeInitializeSocket\n");
- closesocket(listenhandle->ssocket);
- GlobalFree(listenhandle);
- }
- //get two function pointer
- //this has a lower occupancy rate than use win lib directlly
- BOOL IOCPServer::GetAcceptExFuction(void)
- {
- IOCP_DBUG("enter GetAcceptExFuction\n");
- SOCKET s = listenhandle->ssocket;
- if(INVALID_SOCKET == s)
- {
- IOCP_DBUG("WSASocket failed: %d.\n", WSAGetLastError());
- return FALSE;
- }
- // get the fuction pointer for AcceptEx
- DWORD dwBytes = 0;
- GUID GuidAcceptEx = WSAID_ACCEPTEX;
- if (SOCKET_ERROR ==
- WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptEx, sizeof(GUID),
- &lpfnacceptex, sizeof(LPFN_ACCEPTEX), &dwBytes, NULL, NULL))
- {
- IOCP_DBUG("WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, WSAID_ACCEPTEX) failed"
- ": %d.\n", WSAGetLastError());
- return FALSE;
- }
- // get the fuction pointer for GetAcceptExSockAddrs
- GUID GuidAcceptExSockAddrs = WSAID_GETACCEPTEXSOCKADDRS;
- if (SOCKET_ERROR ==
- WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, &GuidAcceptExSockAddrs, sizeof(GUID),
- &lpfngetacceptexsockaddrs, sizeof(LPFN_GETACCEPTEXSOCKADDRS), &dwBytes, NULL, NULL))
- {
- IOCP_DBUG("WSAIoctl(SIO_GET_EXTENSION_FUNCTION_POINTER, WSAID_ACCEPTEX) failed"
- ": %d.\n", WSAGetLastError());
- return FALSE;
- }
- return TRUE;
- }
- //worker thread, usually not one,
- //they are busy until exit.
- //because these thread is asynchronous,
- //so one socket can have more than one io.
- DWORD WINAPI IOCPServer::WorkerThread(LPVOID lpParam)
- {
- IOCP_DBUG("enter WorkerThread\n");
- IOCPServer* temp = (IOCPServer*)lpParam;
- LPWSAOVERLAPPED lpoverlapped = NULL;
- SocketHandle *sockethandle = NULL;
- PerIOData *periodata = NULL;
- DWORD bytestransfered = 0;
- while (TRUE)
- {
- // get the status of comleteport
- BOOL ret =
- GetQueuedCompletionStatus(temp->completeport, &bytestransfered,
- (PULONG_PTR)&sockethandle, &lpoverlapped, INFINITE);
- if(FALSE == ret)
- {
- int sockerror = WSAGetLastError();
- IOCP_DBUG("GetQueuedCompletionStatus failed: %d.\n", sockerror);
- if(64 == sockerror) //host is down, client close abnormally
- {
- if(temp->listenhandle != sockethandle)
- {
- IOCP_DBUG("client %s:%d is DOWN abnormally, we need delete the handle\n",
- inet_ntoa(sockethandle->ssocketaddr.sin_addr),
- ntohs(sockethandle->ssocketaddr.sin_port));
- // the destructor of sockethandle do recycle
- delete sockethandle;
- }else
- {
- IOCP_DBUG("something wrong in listenhandle, you need restart the process");
- }
- }
- continue;
- }
- // read the data in completeport
- periodata = CONTAINING_RECORD(lpoverlapped, PerIOData, overlapped);
- if(NULL == periodata)
- {
- IOCP_DBUG("CONTAINING_RECORD failed: %d.\n", WSAGetLastError());
- continue;
- }
- // free io and handle, if client close the socket
- // all normal handle and io free here, if there is no mistake
- if((0 == bytestransfered) &&
- ( PerIOData::RECV_POSTED == periodata->operationtype
- || PerIOData::SEND_POSTED == periodata->operationtype))
- {
- IOCP_DBUG("client %s:%d has close the socket, we need close the connection\n",
- inet_ntoa(sockethandle->ssocketaddr.sin_addr),
- ntohs(sockethandle->ssocketaddr.sin_port));
- // the destructor of sockethandle do recycle
- delete sockethandle;
- continue;
- }
- switch(periodata->operationtype)
- {
- // AcceptEx
- case PerIOData::ACCEPT_POSTED:
- {
- IOCP_DBUG("recv the ACCEPT_POSTED signal\n");
- if(FALSE == temp->DoAcceptEx(periodata))
- {
- // here sockethandle is listenhandle
- // periodata is listeniodata
- // MUST NOT release them here, let master thread do the bad thing
- // JUST re post
- temp->PostAcceptEx(periodata);
- }
- }
- break;
- // RECV
- case PerIOData::RECV_POSTED:
- {
- IOCP_DBUG("recv the RECV_POSTED signal\n");
- // re post, if failed
- // MUST not close socket here
- // because one socket may have more io in use
- if(FALSE == temp->DoRecv(sockethandle, periodata))
- {
- //MUST not force to release the periodata which hold overlapped struct
- //re post
- temp->PostRecv(sockethandle, periodata);
- }
- }
- break;
- // SEND
- case PerIOData::SEND_POSTED:
- {
- IOCP_DBUG("recv the SEND_POSTED signal\n");
- // re post, if failed
- // no need to construct new io, send use the same one with recv
- if(FALSE == temp->DoSend(sockethandle, periodata))
- {
- // MUST not close socket here
- // because one socket may have more io in use
- //MUST not force to release the periodata which hold overlapped struct
- temp->PostSend(sockethandle, periodata);
- }
- }
- break;
- case PerIOData::NULL_POSTED:
- default:
- IOCP_DBUG("MUST exist a problem, the last error: %d.\n", WSAGetLastError());
- break;
- } //switch
- }//while
- return 0;
- }
- // post AcceptEx
- void IOCPServer::PostAcceptEx(PerIOData* listeniodata)
- {
- IOCP_DBUG("enter PostAcceptEx\n");
- if(NULL == listeniodata)
- {
- IOCP_DBUG("this is a fatal mistake, almost no way to reach here\n");
- return;
- }
- // need new socket before AcceptEx
- listeniodata->acceptsocket = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
- if(INVALID_SOCKET == listeniodata->acceptsocket)
- {
- IOCP_DBUG("WSASocket failed: %d.\n", WSAGetLastError());
- return;
- }
- // set the connect delay for 3 sec
- // because AcceptEx will not return, if client just connect but send no bytes
- // win7 do not support SO_CONNECT_TIME to prevent DDOS
- // instead, dwReceiveDataLength should be ((sizeof(SOCKADDR_IN)+16)*2)
- // only get the local and remote addr
- /*
- INT timeout = 3;
- if (SOCKET_ERROR ==
- setsockopt(listeniodata->acceptsocket, SOL_SOCKET, SO_CONNECT_TIME,
- (const char*)&timeout, sizeof(timeout)))
- {
- IOCP_DBUG("setsockopt(SO_CONNECT_TIME) failed: %d.\n", WSAGetLastError());
- return;
- }
- */
- // do some clean
- // make operationtype signal be ACCEPT_POSTED
- listeniodata->ResetIO();
- listeniodata->operationtype = PerIOData::ACCEPT_POSTED;
- DWORD dwBytes = 0;
- if (FALSE ==
- lpfnacceptex(listenhandle->ssocket, listeniodata->acceptsocket, listeniodata->databuf.buf,
- 0, sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16,
- &dwBytes, &listeniodata->overlapped))
- {
- if (WSA_IO_PENDING != WSAGetLastError())
- {
- IOCP_DBUG("AcceptEx() falied: %d.\n", WSAGetLastError());
- closesocket(listeniodata->acceptsocket);
- listeniodata->acceptsocket = -1;
- return;
- }
- }
- return;
- }
- BOOL IOCPServer::DoAcceptEx(PerIOData* listeniodata)
- {
- /* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */
- // win7 do not support SO_UPDATE_ACCEPT_CONTEXT
- /*
- if (SOCKET_ERROR ==
- setsockopt(listeniodata->acceptsocket, SOL_SOCKET,
- SO_UPDATE_ACCEPT_CONTEXT, (char *)&listenhandle->ssocket, sizeof(SOCKET)))
- {
- IOCP_DBUG("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed: %d.\n", WSAGetLastError());
- return FALSE;
- }
- */
- //get the addr for client which connect me
- //for AcceptEx, Should use GetAcceptExSockAddrs
- SOCKADDR_IN *lplocalsockaddr = NULL;
- SOCKADDR_IN *lpremotesockaddr = NULL;
- int localsockaddrlen = sizeof(SOCKADDR_IN);
- int remotesockaddrlen = sizeof(SOCKADDR_IN);
- lpfngetacceptexsockaddrs(listeniodata->databuf.buf, 0,
- sizeof(SOCKADDR_IN)+16, sizeof(SOCKADDR_IN)+16,
- (LPSOCKADDR*)&lplocalsockaddr, &localsockaddrlen,
- (LPSOCKADDR*)&lpremotesockaddr, &remotesockaddrlen);
- SocketHandle *connecthandle = new SocketHandle();
- connecthandle->ssocket = listeniodata->acceptsocket;
- memcpy(&connecthandle->ssocketaddr, lpremotesockaddr, sizeof(SOCKADDR_IN));
- if (NULL == CreateIoCompletionPort((HANDLE)connecthandle->ssocket, completeport, (ULONG_PTR)connecthandle, 0))
- {
- IOCP_DBUG("CreateIoCompletionPort() failed: %d.\n", WSAGetLastError());
- return FALSE;
- }
- // usually we need to recv something, so post recv
- // we new connecthandle and connectiodata here first
- PerIOData *connectiodata = connecthandle->GetNewIOData();
- PostRecv(connecthandle, connectiodata);
- //post new one
- PerIOData *newlisteniodata = listenhandle->GetNewIOData();
- PostAcceptEx(newlisteniodata);
- return TRUE;
- }
- // post recv
- void IOCPServer::PostRecv(SocketHandle* connecthandle, PerIOData* connectiodata)
- {
- IOCP_DBUG("enter PostRecv\n");
- if(NULL == connecthandle || NULL == connectiodata)
- {
- IOCP_DBUG("this is a fatal mistake, almost no way to reach here\n");
- return;
- }
- // do some clean
- // make operationtype signal be RECV_POSTED
- DWORD flags = 0;
- DWORD recvbytes = 0;
- connectiodata->ResetIO();
- connectiodata->operationtype = PerIOData::RECV_POSTED;
- //recv the message
- int nbytes = WSARecv(connecthandle->ssocket, &connectiodata->databuf, 1,
- &recvbytes, &flags, &connectiodata->overlapped, NULL );
- if ((SOCKET_ERROR == nbytes) && (WSA_IO_PENDING != WSAGetLastError()))
- {
- IOCP_DBUG("WSARecv() failed: %d.\n", WSAGetLastError());
- return;
- }
- return;
- }
- // do after recv
- BOOL IOCPServer::DoRecv(SocketHandle* connecthandle, PerIOData* connectiodata)
- {
- IOCP_DBUG("enter DoRecv\n");
- if(NULL == connecthandle || NULL == connectiodata)
- {
- IOCP_DBUG("this is a fatal mistake, almost no way to reach here\n");
- return FALSE;
- }
- IOCP_DBUG("receive from %s:%d, message: %s.\n",
- inet_ntoa(connecthandle->ssocketaddr.sin_addr),
- ntohs(connecthandle->ssocketaddr.sin_port),connectiodata->databuf.buf);
- //TODO
- //process the massage
- // usually we need send after recv
- PostSend(connecthandle, connectiodata);
- // handle recv signal complete, post new one
- //MUST not force to release the periodata which hold overlapped struct
- //new one periodata
- PerIOData *newconnectiodata = connecthandle->GetNewIOData();
- PostRecv(connecthandle, newconnectiodata);
- return TRUE;
- }
- //post send
- void IOCPServer::PostSend(SocketHandle* connecthandle, PerIOData* connectiodata)
- {
- IOCP_DBUG("enter PostSend\n");
- if(NULL == connecthandle || NULL == connectiodata)
- {
- IOCP_DBUG("this is a fatal mistake, almost no way to reach here\n");
- return;
- }
- // do some clean
- // make operationtype signal be SEND_POSTED
- DWORD flags = 0;
- DWORD sendbytes = 0;
- connectiodata->ResetIO();
- connectiodata->operationtype = PerIOData::SEND_POSTED;
- //TODO
- //build the message
- char *temp = "just for test";
- strncpy_s(connectiodata->databuf.buf, connectiodata->databuf.len, temp, strlen(temp));
- *(connectiodata->databuf.buf + strlen(temp)) = 0;
- //send the message
- int nbytes = WSASend(connecthandle->ssocket, &connectiodata->databuf, 1,
- &sendbytes, flags, &connectiodata->overlapped, NULL );
- if ((SOCKET_ERROR == nbytes) && (WSA_IO_PENDING != WSAGetLastError()))
- {
- IOCP_DBUG("WSASend() failed: %d.\n", WSAGetLastError());
- return;
- }
- return;
- }
- //DO after send
- BOOL IOCPServer::DoSend(SocketHandle* connecthandle, PerIOData* connectiodata)
- {
- IOCP_DBUG("enter DoSend\n");
- if(NULL == connecthandle || NULL == connectiodata)
- {
- IOCP_DBUG("this is a fatal mistake, almost no way to reach here\n");
- return FALSE;
- }
- IOCP_DBUG("send to %s:%d, message: %s.\n",
- inet_ntoa(connecthandle->ssocketaddr.sin_addr),
- ntohs(connecthandle->ssocketaddr.sin_port),connectiodata->databuf.buf );
- //just do it in the end of one io here
- //this is dangerous in somewhere else
- connecthandle->RemoveIOData(connectiodata);
- return TRUE;
- }
HttpServer\SocketHandle.h:
- #pragma once
- #include "stdafx.h"
- #include "PerIOData.h"
- class SocketHandle
- {
- public:
- SocketHandle(void);
- virtual ~SocketHandle(void);
- public:
- //the socket and its addr the handle hold
- SOCKET ssocket;
- SOCKADDR_IN ssocketaddr;
- //the io list belong to me
- list<PerIOData*> sockiodata;
- public:
- PerIOData* GetNewIOData(void);
- // check the cpp source file for the reason
- void RemoveIOData(PerIOData* speriodata);
- };
HttpServer\SocketHandle.cpp:
- #include "stdafx.h"
- #include "SocketHandle.h"
- SocketHandle::SocketHandle(void)
- {
- ssocket = INVALID_SOCKET;
- ZeroMemory(&ssocketaddr, sizeof(ssocketaddr));
- }
- SocketHandle::~SocketHandle(void)
- {
- //close the socket
- if( ssocket!=INVALID_SOCKET )
- {
- closesocket(ssocket);
- ssocket = INVALID_SOCKET;
- }
- // release the list
- list <PerIOData*>::iterator myIterator = sockiodata.begin();
- for ( ; myIterator != sockiodata.end(); myIterator++)
- {
- delete *myIterator;
- *myIterator = NULL;
- }
- sockiodata.clear();
- }
- // new iodata and add it in list
- //return new io which will be used
- PerIOData* SocketHandle::GetNewIOData(void)
- {
- PerIOData* speriodata = new PerIOData();
- sockiodata.push_back(speriodata);
- return speriodata;
- }
- //delete the io transfered and erase it form list
- //MUST not force to release the periodata which hold overlapped struct
- //let the deconstruct func do the recycle
- //we need it just in the end of one io's life.
- //just for case, some ugly client keep sending message to me with no interval
- void SocketHandle::RemoveIOData(PerIOData* speriodata)
- {
- if(NULL == speriodata)
- {
- IOCP_DBUG("you just transfer a NULL pointer\n");
- return;
- }
- list <PerIOData*>::iterator myIterator = sockiodata.begin();
- for ( ; myIterator != sockiodata.end(); myIterator++)
- {
- if(*myIterator == speriodata)
- {
- delete *myIterator;
- *myIterator = NULL;
- sockiodata.erase(myIterator);
- break;
- }
- }
- }
HttpServer\PerIOData.h:
- #pragma once
- #include "stdafx.h"
- // per io data for socket handle
- // one socket can has more than one io
- //WSAOVERLAPPED struct must be the first in memory of this class
- // so this class and its base class MUST not have virtual func
- class PerIOData
- {
- public:
- PerIOData(void);
- ~PerIOData(void);
- public:
- // overlapped struct, must be in the first place of class memory
- WSAOVERLAPPED overlapped;
- //just for ACCEPT_POSTED signal
- SOCKET acceptsocket;
- //the buffer of me
- WSABUF databuf;
- //the signal belong to me
- DWORD operationtype;
- public:
- void ResetIO(void);
- // this four signal is the public signal for other class, IOCPServer etc..
- // ACCEPT_POSTED - signal for accept
- // RECV_POSTED - signal for recv
- // SEND_POSTED - signal for send
- // NULL_POSTED - terminal signal, no meaning
- //
- public:
- typedef enum
- {
- ACCEPT_POSTED,
- RECV_POSTED,
- SEND_POSTED,
- NULL_POSTED
- }OPERATION_TYPE;
- };
HttpServer\PerIOData.cpp:
- #include "stdafx.h"
- #include "PerIOData.h"
- // default buffer size
- #define BUFFERSIZE 1024
- //new buffer and Initialize
- PerIOData::PerIOData(void):
- operationtype(NULL_POSTED),
- acceptsocket(INVALID_SOCKET)
- {
- ZeroMemory(&overlapped, sizeof(overlapped));
- databuf.buf = new char[BUFFERSIZE];
- databuf.len = BUFFERSIZE;
- ZeroMemory(databuf.buf, databuf.len);
- }
- //delete the buffer
- PerIOData::~PerIOData(void)
- {
- if(NULL != databuf.buf)
- {
- delete [] databuf.buf;
- databuf.buf = NULL;
- }
- }
- //reset buffer and overlapped of io
- void PerIOData::ResetIO(void)
- {
- databuf.len = BUFFERSIZE;
- ZeroMemory(databuf.buf, databuf.len);
- ZeroMemory(&overlapped, sizeof(WSAOVERLAPPED));
- }
- 本文转自:http://blog.csdn.net/crasyangel/article/details/40458123