Reputation: 1534
I understand that if I issue an AcceptEx
call, through a function pointer as recommended by the documentation, then if I specify a receiver buffer size, the call will not complete until some data is sent:
if (!lpfnAcceptEx(sockListen,
sockAccept,
PerIoData->Buffer,
DATA_BUFSIZE - ((sizeof(SOCKADDR_IN) + 16) * 2), /* receive buffer size */
sizeof(SOCKADDR_IN) + 16,
sizeof(SOCKADDR_IN) + 16,
&dwBytes,
&(PerIoData->Overlapped)
))
{
DWORD dwLastError = GetLastError();
// Handle error
}
From MSDN
If a receive buffer is provided, the overlapped operation will not complete until a connection is accepted and data is read. Use the getsockopt function with the SO_CONNECT_TIME option to check whether a connection has been accepted.
If the socket is not connected, the getsockopt returns 0xFFFFFFFF. Applications that check whether the overlapped operation has completed, in combination with the SO_CONNECT_TIME option, can determine that a connection has been accepted but no data has been received.
It is recommended such connections be terminated by closing the accepted socket, which forces the AcceptEx function call to complete with an error.
Now, this seems to state that I should forcibly close the socket. However, my book "Network Programming for Microsoft Windows - Second Edition" states similar facts, but goes on to say
As a word of warning, applications should not under any circumstances close a client socket handle used in an AcceptEx call that has not been accepted because it can lead to memory leaks. For performance reasons, the kernel-mode structures associated with an AcceptEx call will not be cleaned up when the unconnected client handle is closed until a new client connection is established or until the listening socket is closed.
So I'm not supposed to close it now?? I'm confused.
Two questions:
1) If a socket has not fully completed AcceptEx
, I get back 0xFFFFFFFF from getsockopt
. This makes it a candidate for foribly closing. But how am I supposed to know how long it has been sitting in this state? I can't add my own timing logic because I don't know when the accept was made because my completion port routine hasn't completed!
2) When I figure out if I need to close the socket, how do I do it? Is closesocket()
enough?
Upvotes: 0
Views: 1074
Reputation: 33754
1) If a socket has not fully completed
AcceptEx
, I get back0xFFFFFFFF
fromgetsockopt
. This makes it a candidate for forcibly closing.
no. this is mistake. if you get 0xFFFFFFFF
this mean that client not conect to socket. it still wait for connection. we need stop this operation, only if we decide stop listening on port at all. otherwise we not need close this socket or cancel this i/o
But how am I supposed to know how long it has been sitting in this state? I can't add my own timing logic because I don't know when the accept was made because my completion port routine hasn't completed!
but getsockopt
with SO_CONNECT_TIME
and returns the number of seconds a socket has been connected:
so if this number is 0xFFFFFFFF
- AcceptEx
still wait for connection and must not be closed/canceled. otherwise (we got another value) - this is number of seconds client already connected. look example of code
so you can periodically check sockets - if you got N (!=-1
) seconds from getsockopt( s, SOL_SOCKET, SO_CONNECT_TIME, (char *)&seconds, (PINT)&bytes)
- this mean that client already N second connected to your socket but yet not send any data. exactly This (when N become too large) makes it a candidate for forcibly closing. but not -1 (0xFFFFFFFF)
value.
So I'm not supposed to close it now?? I'm confused.
you wrong understand. between the two pieces of text there is no contradiction:
... will not be cleaned up when the unconnected client handle is closed ...
note that here say about closing handle used AcceptEx
while it still in unconnected state.
It is recommended such (connected but no data has been received) connections be terminated by closing the accepted socket
so here say about close already connected socket.
so you really need close already connected socket, where too long no data has been received. how long (in seconds) socket connected - you got via SO_CONNECT_TIME
however from my option use receive buffer in AcceptEx
not a good idea. better explicit call WSARecv
after client connect. yes, this is additional call to kernel. but from another side, if you use receive buffer in AcceptEx
- you need periodically call getsockopt
(and this is call to kernel !) on every listen socket. so instead one call on socket, where AcceptEx
completed - you will be need do N calls to getsockopt
every T time period. when AcceptEx
complete just after client connect - you can yourself save time of connection and periodically check this time yourself. but for this you not need be call to kernel and this will be much more faster. time you can get say via GetTickCount64
2) When I figure out if I need to close the socket, how do I do it? Is
closesocket()
enough?
yes closesocket()
is need and enough
Upvotes: 1
Reputation: 1534
OK I found this out myself after examining the code posted by Len Holgate at this link.
Basically we need to store all the SOCKET
objects (that we create in to pass to the AcceptEx
function pointer that we obtain as shown above) in order to iterate through them. Programming for Microsoft Windows - Second Edition tells us that a good time to iterate through pending connections is when we have more connections wanting to be accepted than we do outstanding AcceptEx
calls. We can determine if that is the case as follows:
WSAEVENT NewEvent = CreateEvent(0, FALSE, TRUE, 0); // Auto reset event
WSAEventSelect(sockListen, NewEvent, FD_ACCEPT);
if (::WaitForSingleObject(NewEvent, INFINITE) == WAIT_OBJECT_0)
// Need to post an AcceptEx
Note the use of an auto-reset event, not a manual one as created by WSACreateEvent()
. Now, after posting the AcceptEx
, we can loop through our pending sockets, checking the connected duration on each one:
// Get the time for which this socket has been connected
::getsockopt(sock, SOL_SOCKET, SO_CONNECT_TIME, (char *)&nSeconds, &nBytes);
//
// If we decide the socket has been open for long enough, set SO_LINGER then close it
//
LINGER lingerStruct; // *
lingerStruct.l_onoff = 1; // Leave socket open...
lingerStruct.l_linger = 0; //...for 0 seconds after closesocket() is called
::setsockopt(sock, SOL_SOCKET, SO_LINGER, (char *)&lingerStruct, sizeof(lingerStruct));
closesocket(sock);
(*) See here for why this is required.
The last thing to do is remove the SOCKET
from whatever storage we are keeping it in when the AcceptEx
call completes.
Upvotes: 0