Re: proper way to close a socket?
Bill,
Two points:
First, dealing with async, you need to watch for your events and they
need to be handled.
Second, the subject "proper way to close a socket" is important to
understand the basics here because not doing it properly can cause
MORE unexpected events.
The proper way to close a socket is to use whats called:
TCP Half Closed
And you need to make sure your MFC Async socket class implementation
supports it as well.
Far too many applications do not do this correctly and have all sorts
of issues, including unexpected reconnection attemps (RESET).
The short end of the logic is that you are telling the other side
"Hey I am closing. Stop sending me data"
"Hey I am closing. I'm not going to be sending more data"
or both, but its half closed because you are closed but the other end
is not or vice-versa. So even if you close, the other end may not
finished sending/receiving and if you don't flush the I/O, then you
can get TCP resets.
I'm pretty sure MSDN had a lengthy discussion about this the last time
I visited the issue earlier in the decade when new things were
happening in our internet world:
- higher bandwidths, fast receiver, slower sends, so there
new timing issues here.
- broken software that didn't see the problem under slower
speeds (or same speeds) or did not flush 1 last byte or so.
Apache and IE has this problem, remember the famous "Page Not Found"
issue with IE? It was all related to incorrect usage of closing
sockets and half close socket teardown concepts.
The book "TCP/IP Illustrated Volume 1," specifically talks about "TCP
Half Close" operations and how so many applications are broken because
they done use it.
See shutdown() command.
See SO_LINGER
Google: sockets half-close
See http://msdn.microsoft.com/en-us/library/ms738547(VS.85).aspx
Graceful Shutdown, Linger Options, and Socket Closure
By the way, it is really not that hard. When closing, you simply need
to FLUSH any reads. For example, here the basic idea:
// HalfCloseSocket() performs a TCP Half Close by calling shutdown()
// which signals the remote that no more data is going to be
// sent (FIN signal). HalfCloseSocket() then goes into a
// recv() loop to wait for the remote to acknowledge the close.
// This acknowledgment comes as a recv() return value
// of zero (less).
BOOL HalfCloseSocket(SOCKET socket)
{
if (shutdown(socket,SD_SENT) != 0) {
return FALSE;
}
int ret = 0;
int msecs = 10; // poor man sanity check
char buf[8*1024];
while ((ret = recv(socket, buf,sizeof(buf),0)) > 0) {
buf[0] = 0;
buf[1] = 0;
msecs--;
if (msecs == 0) break;
}
return closesocket(socket);
}
---
Bill < wrote:
Geoff,
The code I showed was from MFC, not from me. So I'm not doing any polling. I
pasted that in to show where the ASSERT is coming from.
My code is deleting the socket object, which according to my comment will
also close the connection.
delete m_pClientSocket; // this will also close the connection
m_pClientSocket = NULL;
The help says "The socket object's destructor calls Close for you." so I
guess that's why I think I only need to delete the object.
I do respond to OnClose().
I just put some breakpoints at OnClose(), OnConnect() and OnAccept().
Neither is being hit when I try to connect to a non-existent IP and port,
whether the ASSERT happens or not. The call stack looks like below, which
only tells me it's not coming directly from a call in my code. It looks like
the systems was preparing to do a callback.
Oh, I see if I am patient, I will get a call to OnConnect() with an 10060
(WSAETIMEDOUT) error about 20 seconds after initiating the conenct. I do
have to handle that differently.
I was thinking that maybe the timeout callback was coming in just as I was
deleting the socket object. But it doesn't seem likely as I can get the
ASSERT long before the 20 second timeout occurs. Any way I can determine
what the callback would have been had it not crashed? wParam = 1764 and
lParam = 657850384 or hex 27360010.
Bill
CAsyncSocket::DoCallBack(unsigned int 1764, long 657850384) line 513 + 25
bytes
CSocket::ProcessAuxQueue() line 823
CSocketWnd::OnSocketNotify(unsigned int 1764, long 657850384) line 1127
CWnd::OnWndMsg(unsigned int 883, unsigned int 1764, long 657850384, long *
0x0012f560) line 1815 + 17 bytes
CWnd::WindowProc(unsigned int 883, unsigned int 1764, long 657850384) line
1585 + 30 bytes
AfxCallWndProc(CWnd * 0x009f5798 {CSocketWnd hWnd=0x000110d6}, HWND__ *
0x000110d6, unsigned int 883, unsigned int 1764, long 657850384) line 215 +
26 bytes
AfxWndProc(HWND__ * 0x000110d6, unsigned int 883, unsigned int 1764, long
657850384) line 368
AfxWndProcBase(HWND__ * 0x000110d6, unsigned int 883, unsigned int 1764,
long 657850384) line 220 + 21 bytes
USER32! 7e418734()
USER32! 7e418816()
USER32! 7e4189cd()
USER32! 7e4196c7()
CWinThread::PumpMessage() line 853
CWnd::RunModalLoop(unsigned long 4) line 3478 + 19 bytes
CDialog::DoModal() line 539 + 12 bytes
CSocketApp::InitInstance() line 65 + 11 bytes
AfxWinMain(HINSTANCE__ * 0x00400000, HINSTANCE__ * 0x00000000, char *
0x00141f1e, int 1) line 39 + 11 bytes
SOCKET! 0040dec8() line 30
WinMainCRTStartup() line 330 + 54 bytes
"Geoff" <geoff@invalid.invalid> wrote in message
news:e6cep51nscc07242lblsa06lublh53030h@4ax.com...
On Wed, 10 Mar 2010 10:42:21 +0800, "Bill" <<don't want more spam>>
wrote:
Did I ask this in the right newsgroup? (Just not sure if there is a more
appropriate palce to ask this.) Tnx.
Dr. Joe usually has a lot to say about callbacks and about
CAsyncSocket but he must be busy polishing his new MVP award.
I am not sure why you think you need to poll the handle. What you
should be doing is either closing it with CAsyncSocket::Close when you
are done with your session on your end or else responding to
CAsyncSocket::OnClose and doing any cleanup if it is closed from the
other end.
--
HLS