Re: proper way to close a socket?
I have been doing socket programming successfully for many years and not =
once have I had
to do any of this! I have never used select(), have never had to do a =
PeekMessage() loop,
never once had the thread block. I just never used synchronous sockets=
(note that
CSocket is known to have bugs and should never be used for real programs)=
..
<sidenote>
I have been seeing real posting problems on this newsgroup. This
message of yours appears here via Google Group, but not via my TBIRD
nor Opera News Reader. I posted several responses which to Bill, Peter
and you that simply didn't get posted. I am reposting via google, my
apology for any dupes.
</sidenote>
Joe,
sockets are inherently synchronous. The blocking connect() function
has a 20-30 second timeout with two results:
- success
- timeout (use GetLastError() to get reason)
In practice, you can make this asynchronous, by calling:
u_long onoff = 1;
ioctlsocket(hSocket, FIONBIO, &onoff);
at which points its behaves like a OverLapped I/O coding framework
where you call connect(), it returns immediately with an error of
WSAEWOULDBLOCK, at which point you would wait on the "completion". In
this case, this is where the ::select() function is used:
::connect()
if (GetLastError() == WSAEWOULDBLOCK) {
// prepare select with a timeout and
// the type of event to look for. For
// connect, you are looking for a read
// and error event. read means it completed
// successfully. You can tell select to
// with forever here or in small chucks
// which means you need a loop here.
int nTimeout = 5; // timeoout value
for (;;) {
nTimeout--;
if (nTimeout <= 0) {
err = WSAETIMEDOUT;
return FALSE;
}
fd_set efds; fd_set wfds;
FD_ZERO(&efds); FD_ZERO(&wfds);
FD_SET(hSocket, &efds);
FD_SET(hSocket, &wfds);
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
int rc = select(0, NULL, &wfds, &efds, &tv);
if (rc > 0) {
if (FD_ISSET(hSocket,&wfds)) {
// connected!
err = 0;
break;
}
if (FD_ISSET(hSocket,&efds)) {
err = WSAEHOSTUNREACH;
return FALSE;
}
}
if (rc == SOCKET_ERROR) {
err = WSAGetLastError();
if (err != WSAEWOULDBLOCK) return FALSE;
}
}
}
Another approach (as used in many high end production software) is to
put this in a 2nd THREAD and then allow the blocked connect() to
timeout/succeed and then use some IPC method such as WM_
notification, call back or kernel object signal to the main thread.
The Microsoft Winsock Level 2 functions does precisely that with its
WSAxxxxxx functions and the WSAAsyncSelect() function to prepare
sending WM_SOCKET_NOTIFY messages.
The point is, when the wrapper CAsyncSocket::Connect() function is
called, it is asynchronous because the CAsyncSocket::Create() by
default prepares it for async.
As expected by native ::connect() which MS WINSOCK level 2 calls as
well, only two results are possible - success or timeout, simply
WINSOCK2 layer is doing something similar with a "I/O pending" concept
as shown above. There is no other way around it. In this design, its
going to issue WM_SOCKET_NOTIFY messages to the various
CAsyncSocket::OnXXXXXXX() handlers.
So to get an earlier timeout you have to monitor the connection by
doing the same as above yourself or have a loop where it will check
the ::select status every X msecs or so after the
CAsyncSocket::Connect() is called. Otherwise, you just have to wait
until the WINSOCK2 call to connect() completes.
Now, you could interrupt the connect by invalidating the handle,
m_hSocket, directly:
::closesocket(pAsyncSocket->m_hSocket);
that will immediately signal the wait on ::select(); That is what the
inherited CAsyncSocket::Close() will do, plus it will remove any
handlers for the socket. That is why you won't get a OnConnect()
notification for the error condition when you call Close().
The bottom line is that all socket implementations is synchronous
unless you change the socket mode to asynchronous like so:
u_long onoff = 1;
ioctlsocket(hSocket, FIONBIO, &onoff);
At which point you have a similar I/O Pending concept (using ::select)
like you have with WIN32 FILE I/O and preparing it for Overlapping I/
O. It is the Class Wrapper that provides an extra layer for
notification, generally in callback form or messages.
--
HLS