📄 iocpserverex.cpp
字号:
// to be abortive and close the connection.
//
/*
LINGER lingerStruct;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
nRet = setsockopt(sdSocket, SOL_SOCKET, SO_LINGER,
(char *)&lingerStruct, sizeof(lingerStruct));
if( nRet == SOCKET_ERROR ) {
myprintf("setsockopt(SO_LINGER) failed: %d\n", WSAGetLastError());
return(sdSocket);
}
*/
return(sdSocket);
}
//
// Create a listening socket, bind, and set up its listening backlog.
//
BOOL CreateListenSocket(void) {
int nRet = 0;
LINGER lingerStruct;
struct addrinfo hints;
struct addrinfo *addrlocal = NULL;
lingerStruct.l_onoff = 1;
lingerStruct.l_linger = 0;
//
// Resolve the interface
//
memset(&hints, 0, sizeof(hints));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_IP;
if( getaddrinfo(NULL, g_Port, &hints, &addrlocal) != 0 ) {
myprintf("getaddrinfo() failed with error %d\n", WSAGetLastError());
return(FALSE);
}
if( addrlocal == NULL ) {
myprintf("getaddrinfo() failed to resolve/convert the interface\n");
return(FALSE);
}
g_sdListen = CreateSocket();
if( g_sdListen == INVALID_SOCKET) {
freeaddrinfo(addrlocal);
return(FALSE);
}
nRet = bind(g_sdListen, addrlocal->ai_addr, addrlocal->ai_addrlen);
if( nRet == SOCKET_ERROR) {
myprintf("bind() failed: %d\n", WSAGetLastError());
freeaddrinfo(addrlocal);
return(FALSE);
}
nRet = listen(g_sdListen, 5);
if( nRet == SOCKET_ERROR ) {
myprintf("listen() failed: %d\n", WSAGetLastError());
freeaddrinfo(addrlocal);
return(FALSE);
}
freeaddrinfo(addrlocal);
return(TRUE);
}
//
// Create a socket and invoke AcceptEx. Only the original call to to this
// function needs to be added to the IOCP.
//
// If the expected behaviour of connecting client applications is to NOT
// send data right away, then only posting one AcceptEx can cause connection
// attempts to be refused if a client connects without sending some initial
// data (notice that the associated iocpclient does not operate this way
// but instead makes a connection and starts sending data write away).
// This is because the IOCP packet does not get delivered without the initial
// data (as implemented in this sample) thus preventing the worker thread
// from posting another AcceptEx and eventually the backlog value set in
// listen() will be exceeded if clients continue to try to connect.
//
// One technique to address this situation is to simply cause AcceptEx
// to return right away upon accepting a connection without returning any
// data. This can be done by setting dwReceiveDataLength=0 when calling AcceptEx.
//
// Another technique to address this situation is to post multiple calls
// to AcceptEx. Posting multiple calls to AcceptEx is similar in concept to
// increasing the backlog value in listen(), though posting AcceptEx is
// dynamic (i.e. during the course of running your application you can adjust
// the number of AcceptEx calls you post). It is important however to keep
// your backlog value in listen() high in your server to ensure that the
// stack can accept connections even if your application does not get enough
// CPU cycles to repost another AcceptEx under stress conditions.
//
// This sample implements neither of these techniques and is therefore
// susceptible to the behaviour described above.
//
BOOL CreateAcceptSocket(BOOL fUpdateIOCP) {
int nRet = 0;
DWORD dwRecvNumBytes = 0;
DWORD bytes = 0;
//
// GUID to Microsoft specific extensions
//
GUID acceptex_guid = WSAID_ACCEPTEX;
//
//The context for listening socket uses the SockAccept member to store the
//socket for client connection.
//
if( fUpdateIOCP ) {
g_pCtxtListenSocket = UpdateCompletionPort(g_sdListen, ClientIoAccept, FALSE);
if( g_pCtxtListenSocket == NULL ) {
myprintf("failed to update listen socket to IOCP\n");
return(FALSE);
}
// Load the AcceptEx extension function from the provider for this socket
nRet = WSAIoctl(
g_sdListen,
SIO_GET_EXTENSION_FUNCTION_POINTER,
&acceptex_guid,
sizeof(acceptex_guid),
&g_pCtxtListenSocket->fnAcceptEx,
sizeof(g_pCtxtListenSocket->fnAcceptEx),
&bytes,
NULL,
NULL
);
if (nRet == SOCKET_ERROR)
{
myprintf("failed to load AcceptEx: %d\n", WSAGetLastError());
return (FALSE);
}
}
g_pCtxtListenSocket->pIOContext->SocketAccept = CreateSocket();
if( g_pCtxtListenSocket->pIOContext->SocketAccept == INVALID_SOCKET) {
myprintf("failed to create new accept socket\n");
return(FALSE);
}
//
// pay close attention to these parameters and buffer lengths
//
nRet = g_pCtxtListenSocket->fnAcceptEx(g_sdListen, g_pCtxtListenSocket->pIOContext->SocketAccept,
(LPVOID)(g_pCtxtListenSocket->pIOContext->Buffer),
MAX_BUFF_SIZE - (2 * (sizeof(SOCKADDR_STORAGE) + 16)),
sizeof(SOCKADDR_STORAGE) + 16, sizeof(SOCKADDR_STORAGE) + 16,
&dwRecvNumBytes,
(LPOVERLAPPED) &(g_pCtxtListenSocket->pIOContext->Overlapped));
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf("AcceptEx() failed: %d\n", WSAGetLastError());
return(FALSE);
}
return(TRUE);
}
//
// Worker thread that handles all I/O requests on any socket handle added to the IOCP.
//
DWORD WINAPI WorkerThread (LPVOID WorkThreadContext) {
HANDLE hIOCP = (HANDLE)WorkThreadContext;
BOOL bSuccess = FALSE;
int nRet = 0;
LPWSAOVERLAPPED lpOverlapped = NULL;
PPER_SOCKET_CONTEXT lpPerSocketContext = NULL;
PPER_SOCKET_CONTEXT lpAcceptSocketContext = NULL;
PPER_IO_CONTEXT lpIOContext = NULL;
WSABUF buffRecv;
WSABUF buffSend;
DWORD dwRecvNumBytes = 0;
DWORD dwSendNumBytes = 0;
DWORD dwFlags = 0;
DWORD dwIoSize = 0;
while( TRUE ) {
//
// continually loop to service io completion packets
//
bSuccess = GetQueuedCompletionStatus(
hIOCP,
&dwIoSize,
(PDWORD_PTR)&lpPerSocketContext,
(LPOVERLAPPED *)&lpOverlapped,
INFINITE
);
if( !bSuccess )
myprintf("GetQueuedCompletionStatus() failed: %d\n", GetLastError());
if( lpPerSocketContext == NULL ) {
//
// CTRL-C handler used PostQueuedCompletionStatus to post an I/O packet with
// a NULL CompletionKey (or if we get one for any reason). It is time to exit.
//
return(0);
}
if( g_bEndServer ) {
//
// main thread will do all cleanup needed - see finally block
//
return(0);
}
lpIOContext = (PPER_IO_CONTEXT)lpOverlapped;
//
//We should never skip the loop and not post another AcceptEx if the current
//completion packet is for previous AcceptEx
//
if( lpIOContext->IOOperation != ClientIoAccept ) {
if( !bSuccess || (bSuccess && (0 == dwIoSize)) ) {
//
// client connection dropped, continue to service remaining (and possibly
// new) client connections
//
CloseClient(lpPerSocketContext, FALSE);
continue;
}
}
//
// determine what type of IO packet has completed by checking the PER_IO_CONTEXT
// associated with this socket. This will determine what action to take.
//
switch( lpIOContext->IOOperation ) {
case ClientIoAccept:
//
// When the AcceptEx function returns, the socket sAcceptSocket is
// in the default state for a connected socket. The socket sAcceptSocket
// does not inherit the properties of the socket associated with
// sListenSocket parameter until SO_UPDATE_ACCEPT_CONTEXT is set on
// the socket. Use the setsockopt function to set the SO_UPDATE_ACCEPT_CONTEXT
// option, specifying sAcceptSocket as the socket handle and sListenSocket
// as the option value.
//
nRet = setsockopt(
lpPerSocketContext->pIOContext->SocketAccept,
SOL_SOCKET,
SO_UPDATE_ACCEPT_CONTEXT,
(char *)&g_sdListen,
sizeof(g_sdListen)
);
if( nRet == SOCKET_ERROR ) {
//
//just warn user here.
//
myprintf("setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed to update accept socket\n");
WSASetEvent(g_hCleanupEvent[0]);
return(0);
}
lpAcceptSocketContext = UpdateCompletionPort(
lpPerSocketContext->pIOContext->SocketAccept,
ClientIoAccept, TRUE);
if( lpAcceptSocketContext == NULL ) {
//
//just warn user here.
//
myprintf("failed to update accept socket to IOCP\n");
WSASetEvent(g_hCleanupEvent[0]);
return(0);
}
if( dwIoSize ) {
lpAcceptSocketContext->pIOContext->IOOperation = ClientIoWrite;
lpAcceptSocketContext->pIOContext->nTotalBytes = dwIoSize;
lpAcceptSocketContext->pIOContext->nSentBytes = 0;
lpAcceptSocketContext->pIOContext->wsabuf.len = dwIoSize;
CopyMemory(lpAcceptSocketContext->pIOContext->Buffer, lpPerSocketContext->pIOContext->Buffer, sizeof(lpPerSocketContext->pIOContext->Buffer));
lpAcceptSocketContext->pIOContext->wsabuf.buf = lpAcceptSocketContext->pIOContext->Buffer;
nRet = WSASend(
lpPerSocketContext->pIOContext->SocketAccept,
&lpAcceptSocketContext->pIOContext->wsabuf, 1,
&dwSendNumBytes,
0,
&(lpAcceptSocketContext->pIOContext->Overlapped), NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf ("WSASend() failed: %d\n", WSAGetLastError());
CloseClient(lpAcceptSocketContext, FALSE);
} else if( g_bVerbose ) {
myprintf("WorkerThread %d: Socket(%d) AcceptEx completed (%d bytes), Send posted\n",
GetCurrentThreadId(), lpPerSocketContext->Socket, dwIoSize);
}
} else {
//
// AcceptEx completes but doesn't read any data so we need to post
// an outstanding overlapped read.
//
lpAcceptSocketContext->pIOContext->IOOperation = ClientIoRead;
dwRecvNumBytes = 0;
dwFlags = 0;
buffRecv.buf = lpAcceptSocketContext->pIOContext->Buffer,
buffRecv.len = MAX_BUFF_SIZE;
nRet = WSARecv(
lpAcceptSocketContext->Socket,
&buffRecv, 1,
&dwRecvNumBytes,
&dwFlags,
&lpAcceptSocketContext->pIOContext->Overlapped, NULL);
if( nRet == SOCKET_ERROR && (ERROR_IO_PENDING != WSAGetLastError()) ) {
myprintf ("WSARecv() failed: %d\n", WSAGetLastError());
CloseClient(lpAcceptSocketContext, FALSE);
}
}
//
//Time to post another outstanding AcceptEx
//
if( !CreateAcceptSocket(FALSE) ) {
myprintf("Please shut down and reboot the server.\n");
WSASetEvent(g_hCleanupEvent[0]);
return(0);
}
break;
case ClientIoRead:
//
// a read operation has completed, post a write operation to echo the
// data back to the client using the same data buffer.
//
lpIOContext->IOOperation = ClientIoWrite;
lpIOContext->nTotalBytes = dwIoSize;
lpIOContext->nSentBytes = 0;
lpIOContext->wsabuf.len = dwIoSize;
dwFlags = 0;
nRet = WSASend(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -