完成端口IOCP 流程
参考:《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
百度搜索“就爱阅读”,专业资料,生活学习,尽在就爱阅读网92to.com,您的在线图书馆!
来源: http://www.92to.com/bangong/2017/09-26/29107428.html