Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CSocket ASSERTION Failures

10 views
Skip to first unread message

hay...@sierranet.net

unread,
Jan 20, 1997, 3:00:00 AM1/20/97
to

I'm having a problem with a little server I have been working on.
Basically, my plan is to use a CAsyncSocket to be the listen socket
that runs in the main thread of my Win32 program. I have overriden
the OnAccept method of the CAsyncSocket class to accept the connection
with a CSocket object and activate a new thread to handle the actual
connection. Everything works fine until I try to close the CSocket
object. I have tried code similar to the following:

ConnectionThread::EndConnection()
{
m_pSocket->Close();
delete m_pSocket;
}

However, when the Close method is called, my debugging session stops
with an assertion failure in the AsyncSelect method of sockcore.cpp
(MFC source file), and it fails on the line that tests whether or not
the variable m_hSocketWindow is NULL. m_hSocketWindow is a member
variable of an object that is returned from the
AfxGetModuleThreadState() function. Since there is NO documentation
that I can find (nor source code comments) on the assertion failure, I
do not know why this is happening.

I have tried the above code with/without the ShutDown statment, but
to no luck. I also get the same problem if I remove the Close method
call and just delete the CSocket (docs state that Close is called in
destructor). Any help with the above problem would be greatly
appreciated.

Mike


A.Gopal

unread,
Jan 23, 1997, 3:00:00 AM1/23/97
to hay...@sierranet.net

Hi mike,
Your problem could most likely be the following one
read further.

***********************************************************************
PSS ID Number: Q140527
Article last modified on 12-10-1995

2.10 2.20 4.00

WINDOWS NT

---------------------------------------------------------------------
The information in this article applies to:

- The Microsoft Foundation Classes (MFC) included with:
Microsoft Visual C++, 32-bit Edition, versions 2.1, 2.2, 4.0
---------------------------------------------------------------------

SYMPTOMS
========

A multi-threaded application that uses MFC's socket classes encounters a
message box or debug output line that contains an error message similar
to
the following:

For Visual C++ 2.x:

Assertion failed - <app name>:File sockcore.cpp, Line 837

For Visual C++ 4.0:

Assertion failed - <app name>:File sockcore.cpp, Line 1041

CAUSE
=====

Most frequently, this problem is due to the sharing of CSocket objects
between multiple threads.

A CSocket object should be used only in the context of a single thread
because the SOCKET handle encapsulated by a CAsyncSocket object is
stored
in a per-thread handle map. (CSocket is derived from CAsyncSocket.)
Other
information is stored on a per-thread basis, including a hidden
notification window that MFC uses for socket notifications.

The assertion failure line, which can be found in Sockcore.cpp in the
\Msvc20\Mfc\Src directory, is:

ASSERT(pThreadState->m_hSocketWindow != NULL);

This assertion failure occurs because the CSocket object was either
created
or accepted in the context of another thread. The socket notification
window was created in a different thread, and the m_hSocketWindow for
the
current thread is NULL, thus the assertion failure.

RESOLUTION
==========

As already mentioned, a CAsyncSocket object should be used only in the
context of a single thread. If you need to switch the thread that is
accessing a SOCKET connection with another thread, then you should use a
separate CAsyncSocket object in each thread, and use the Detach and
Attach
functions to attach the CAsyncSocket object to the SOCKET handle in the
thread that is about to use the socket. Use this sequence:

1. Use Detach() to detach the CAsyncSocket object from the SOCKET handle
in
the thread that is currently using the CAsyncSocket object.

2. Use Attach() to attach a different CAsyncSocket object to the SOCKET
handle while in the context of the thread in which you wish to begin
accessing the SOCKET connection.

The code shown in the "Code Sample" section of this article shows how to
handle the moment when a listening socket accepts a connection request
and
then begins a new thread to handle the new connection.

NOTE: One concern often arises that socket notification messages might
be
lost between the time the call to Detach() is made and the subsequent
call
to Attach() is made. This is not a concern because of the way socket
notifications are handled. The implementation of CAsyncSocket::Attach()
makes a call to WSAAsyncSelect to enable notifications. As mentioned in
the
documentation for WSAAsyncSelect, if any socket noficiations were
already
pending for the SOCKET, they will be re-posted.

MORE INFORMATION
================

To Accept a socket in the context of one thread and then begin using it
in
the context of another thread, you need to be sure to detach the
CAsyncSocket object in the first thread and attach a different
CAsyncSocket
object in the second thread. The following code snippet shows how to do
it.

Code Sample
-----------

// ...
class CSockThread : public CWinThread
{
// ... Other function and member declarations
protected:
CSocket m_sConnected;
};

SOCKET hConnected;

BOOL CSockThread::InitInstance()
{
// Attach the socket object to the socket handle
// in the context of this thread.
//
m_sConnected.Attach(hConnected);

return TRUE;
}

// This listening socket has been constructed
// in the primary thread.
//
void CListeningSocket::OnAccept(int nErrorCode)
{
// This CSocket object is used just temporarily
// to Accept the incoming connection.
//
CSocket sConnected;
Accept(sConnected);

// Detach the newly accepted socket and save
// the SOCKET handle
hConnected = sConnected.Detach();

// After Detaching it, it should no longer be
// used in the context of this thread

// Start the other thread
AfxBeginThread(RUNTIME_CLASS(CSockThread));
}

REFERENCES
==========

MFC Technical Note #2 - Although this technical note does not directly
address the CAsyncSocket class, it does discuss the mapping of handles
to
objects. The relationship between a SOCKET handle and a CAsyncSocket
object
is maintained in much the same way.

Additional reference words: 2.10 2.20 3.10 3.20 4.00
KBCategory: kbprg kbcode kbprb
KBSubcategory: MfcSockets
=============================================================================
Copyright Microsoft Corporation 1995.

*********************************************************************************

Here are some more assertions that you should be aware of....

*********************************************************************************


PSS ID Number: Q139692
Article last modified on 11-20-1995

1.52 | 2.10 2.20

WINDOWS | WINDOWS NT

---------------------------------------------------------------------
The information in this article applies to:

- The Microsoft Foundation Classes (MFC), included with:

- Microsoft Visual C++ for Windows, version 1.52
- Microsoft Visual C++, 32-bit Edition, versions 2.1, 2.2
---------------------------------------------------------------------

SYMPTOMS
========

An MFC application that uses the socket classes and makes sequential
calls to close and create sockets encounters an assertion failure:

In MFC 3.1 and 3.2, the assertion appears as:

Assertion Failed: <app name>: File sockcore.cpp, Line 475

In MFC 2.52, the assertion appears as:

Assertion Failed: <app name>: File sockcore.cpp, Line 456

CAUSE
=====

MFC maintains a map of dead sockets. (A dead socket is a socket that was
open but that has been closed.) The purpose of this map is described in
further detail in the "More Information" section of this article.

If an application closes and opens sockets in the same thread without
yielding to process messages, then it is possible that an attempt will
be
made to close a socket that has already been closed. MFC does not
properly
handle this because it can only have one entry in the map of dead
sockets.
The resulting assertion failure is checking whether there is already an
entry in the map of dead sockets:

ASSERT(CAsyncSocket::LookupHandle(hSocket, TRUE) == NULL);
// The TRUE means it is looking for a dead socket handle,
// thus the assertion failure occurs.

RESOLUTION
==========

There are four possible resolutions:

- The problem occurs only if you are closing and opening sockets
sequentially without yielding to process messages. If you are doing
this
to send many small packets of data, there might be other approaches
that
would work better. See the "More Information" section of this article
for further details.

-or-

- One pretty simple resolution is to suspend the closing of any sockets
until all sockets have been opened. This prevents any new SOCKET from
being opened (and subsequently closed) that happens to have the same
SOCKET handle as a previously closed socket.

-or-

- You can write a routine to process all pending socket messages
immediately after calling Close and before continuing with code
execution. This could be done, for example, in the Close member
function
of your own CAsyncSocket-derived or CSocket-derived class:

#include <afxpriv.h>

void CMySocket::Close()
{
// For the socket about to be closed.
SOCKET hDeadSocket = m_hSocket;

CAsyncSocket::Close();
// or for CSocket-derived class:
// CSocket::Close();

MSG msg;
CSocket::ProcessAuxQueue();
while(::PeekMessage(&msg,NULL,
WM_SOCKET_NOTIFY,WM_SOCKET_DEAD,PM_REMOVE))
{
::DispatchMessage(&msg);
if( (msg.message==WM_SOCKET_DEAD) &&
((SOCKET)msg.wParam==hDeadSocket))
break;
}
}

The virtual destructor should also be overridden to make sure the
correct Close is called:

CMySocket::~CMySocket()
{
if (m_hSocket != INVALID_SOCKET)
Close();
}

Note that this will cause CAsyncSocket-overriden and
CSocket-overridden
callbacks (for example, OnReceive, OnAccept) to be called for any
currently open sockets that have notification messages pending in the
queue.

-or-

- Restructure the code so that the Close/Open operations are not in a
non-
yielding sequence. This could be done by providing a message handler
for
a user-defined message and by performing a specific operation in that
message handler. Rather than just proceeding with the thread of
execution, a function call that Closes or Destroys a
CAsyncSocket/CSocket object should be followed by a PostMessage with
the
user-defined message. This way all other messages (including the
WM_SOCKET_DEAD message) will be processed before the subsequent
socket
operations are performed.

STATUS
======

Microsoft has confirmed this to be a bug in the Microsoft products
listed
at the beginning of this article. This problem was corrected in the
version
of MFC that ships with Microsoft Visual C++, 32-bit Edition, version
4.0.

MORE INFORMATION
================

Sending Small Packets of Data
-----------------------------

If you are just interested in sending small packets of data back and
forth,
then opening and closing several STREAM type sockets might not be the
best
solution. STREAM sockets were created for sending large amounts of data
reliably. Two alternate solutions (using one persistent connection or
using
DATAGRAM sockets) might be more suitable and would probably provide
better
performance than you would get by opening and closing many STREAM type
socket connections.

Using One Persistent Connection
-------------------------------

If you are sending this data between the same processes, you can do
something like what the Chatter and ChatSrvr samples do. They both have
a
Message Structure defined. They open a socket connection and leave it
open.
Then they just send multiple Message Structures back and forth to each
other through the socket until one or the other side decides to
disconnect
permanently. Then the LastMessage bit is set in the Message Structure
and
sent to the other process. The other process then closes down
gracefully.

Using DATAGRAM Sockets
----------------------

You can open a single unconnected DATAGRAM socket, and use the SendTo
and
ReceiveFrom functions to send little bits of data between processes.

This might be a better approach because it does not require you to use
the
overhead of opening or closing a socket connection for every message you
send. You can just use an unconnected DATAGRAM socket, and use SendTo
when
you need to send the data. Additionally, DATAGRAM sockets are generally
faster than STREAM sockets. The primary catch here is that DATAGRAM
sockets
do not guarantee delivery of your data -- although on many systems, they
are almost as reliable as STREAM sockets.

The WM_SOCKET_DEAD Mechanism
----------------------------

MFC has a mechanism for preventing a problem that occurs when a socket
has
been closed while there were still notification messages in the
application's message queue that were bound for that socket.

The problem is that a new socket could be opened that has the same
handle
as the socket that was closed. The messages that still existed in the
message queue are eventually received, and they appear to be destined
for
the newly opened socket. This can cause a variety of problems. For
example,
OnReceive might be called for a socket that actually has nothing to
receive.

MFC's appraoch to handling this problem uses a special message called
WM_SOCKET_DEAD. Whenever a socket is closed, the following occurs:

1. The SOCKET handle is removed from the map of attached sockets.

2. The SOCKET handle is placed in the map of dead sockets.

3. The WM_SOCKET_DEAD message is posted to the application's message
queue
with a wParam indicating the closed SOCKET handle.

NOTE: These first three steps are implemented in
CAsyncSocket::KillSocket,
in Sockcore.cpp.

4. The message routing mechanism for MFC's hidden socket notification
window ignores all messages for a SOCKET handle while the SOCKET
handle is in the map of dead sockets.

5. The SOCKET handle is not removed from the map of dead sockets until
the
WM_SOCKET_DEAD message is received.

By POSTING the WM_SOCKET_DEAD message, MFC causes all pending socket
messages for that particular SOCKET handle to be ignored until the
WM_SOCKET_DEAD message is received. The effect of this is to ignore any
notifications that were pending for the now-dead socket. Any messages
received after that are notifications for the open socket and those
messages will be properly handled.

Additional reference words: 2.52 2.10 2.20 3.10 3.20 4.00 CSocket
CAsyncSocket
KBCategory: kbprg kbbuglist kbfixlist
KBSubcategory: MfcSockets
=============================================================================
Copyright Microsoft Corporation 1995.

**********************************************************************************

and....



PSS ID Number: Q130944
Article last modified on 01-09-1996

1.52

WINDOWS

---------------------------------------------------------------------
The information in this article applies to:

- The Microsoft Foundation Classes (MFC) included with:
Microsoft Visual C++ for Windows, version 1.52
---------------------------------------------------------------------

SYMPTOMS
========

An application using MFC's socket classes (CSocket and CAsyncSocket)
causes
an assertion failure upon exit. The assertion failure text is similar to
this:

myapp Windows Application: File sockcore.cpp, Line 51, Assertion
Failed!

CAUSE
=====

This assertion failure most likely occurs if any CSocket or CAsyncSocket
objects have not been closed (or destroyed).

RESOLUTION
==========

Ensure that all socket objects in the application are properly closed
and
destroyed. If a socket object has been used but was not destroyed, this
message will occur.

Here is a technique you can use to determine which socket object was not
destroyed:

1. Make sure that tracing has been enabled by running the "MFC Trace
Options" program, which can be found in the Visual C++ group. Select
the
Enable Tracing check box.

2. Debug the application using the Visual Workbench Debugger:

Select Debug.Go

3. When the Assertion Failed! message box appears for this assertion,
select Ignore.

At this point, if you have not destroyed all of your socket objects, the
Visual Workbench Output window will probably list a memory leak for the
particular socket object that has not been destroyed. The message looks
similar to this:

Detected memory leaks!
Dumping objects ->
{2} a CSocket at $DEF0932 m_hSocket = 0x2
m_pbBlocking = $0
m_nConnectError = -1
Object dump complete.

STATUS
======

This behavior is by design.

MORE INFORMATION
================

For more information on how to track down memory leaks, please see the
following article in the Microsoft Knowledge Base:

ARTICLE-ID: Q122307
TITLE : Tracking Down Memory Leaks with _afxBreakAlloc

Using the techniques shown in Q122307, you can determine where the
socket
object was allocated.

Sockets require a window to receive notification messages when socket
events occur (when data is ready to be received on the socket). MFC
manages
this notification window for you when you use the MFC socket classes.

This window is created by MFC when your application uses a socket
object.
When all of the socket objects have been closed, MFC destroys the
notification window.

The assertion failure message is generated by the following line:

ASSERT(_afxSockState->hSocketWindow == NULL);

This assertion is verifying that the socket notification window has been
destroyed. Because this window is destroyed when all of the socket
objects
have been destroyed, you have most likely created and used a socket
object
but never destroyed it.

A common scenario where this might occur is with a server application
that
opens a listening socket, and the listening socket is left open
throughout
the execution of the application. In this scenario, you may easily
overlook
the need to destroy the socket object. For example:

BOOL CMyApp::InitInstance()
{
// ...
m_pSock = new CListeningSocket;
m_pSock->Create(nPort);
m_pSock->Listen();
// ...
return CWinApp::InitInstance();
}

This is a common sequence of events, but you must remember to destroy
the
socket object before exiting the application. For example:

int CMyApp::ExitInstance()
{
delete m_pSock;
return CWinapp::ExitInstance();
}

Additional reference words: 1.52 2.52
KBCategory: kbprg kberrmsg kbprb kbtshoot
KBSubcategory: MfcSockets
=============================================================================
Copyright Microsoft Corporation 1996.



and....


PSS ID Number: Q139693
Article last modified on 11-20-1995

1.52 | 2.10 2.20

WINDOWS | WINDOWS NT

---------------------------------------------------------------------
The information in this article applies to:

- The Microsoft Foundation Classes (MFC) included with:

- Microsoft Visual C++ for Windows, version 1.52
- Microsoft Visual C++, 32-bit Edition, version 2.1 and 2.2
---------------------------------------------------------------------

SYMPTOMS
========

An MFC application that uses the socket classes fails with a message
similar to the following:

In MFC 3.1 or 3.2, the assertion appears as:

Assertion Failed: <app name>: File sockcore.cpp, Line 505

In MFC 2.52, the assertion appears as:

Assertion Failed: <app name>: File sockcore.cpp, Line 484

CAUSE
=====

When all sockets are closed, the socket handle maps are emptied and the
socket notification window is destroyed.

MFC maintains an auxiliary queue of socket notification messages. If any
messages were remaining in this queue when the last socket has been
closed,
then this assertion failure will occur when any new sockets are opened
and
an attempt is made to process these left-over notifications.

RESOLUTION
==========

Purge the auxiliary queue of all messages when the last socket is
closed.
In this context, last does not mean previous; it means the socket that
was
closed, leaving no sockets open for that thread.

This can be remedied by overriding the CAsyncSocket::Close member
function
in your CAsyncSocket-derived or CSocket-derived class. The following
implementation of this override will take care of the problem:

void CMySocket::Close()
{
// If Deriving from CSocket, then use:
CSocket::Close();
// Otherwise, use:
// CAsyncSocket::Close();
AFX_THREAD_STATE* pThreadState = AfxGetThreadState();
if (pThreadState->m_mapSocketHandle.IsEmpty())
{ // **** LAST SOCKET ****
while (!pThreadState->m_listSocketNotifications.IsEmpty())
delete pThreadState->m_listSocketNotifications.RemoveHead();
pThreadState->m_listSocketNotifications.RemoveAll();
}
}

To handle the case where a socket object is deleted before Close is
called,
also override the virtual destructor to make sure the correct version of
Close is called:

CMySocket::~CMySocket()
{
if (m_hSocket != INVALID_SOCKET)
Close();
}

STATUS
======

Microsoft has confirmed this to be a bug in the Microsoft products
listed
at the beginning of this article. This problem was corrected in the
version
of MFC that ships with Microsoft Visual C++, 32-bit Edition, version
4.0.

Additional reference words: 1.52 2.10 2.20 2.52 3.10 3.20 4.00 CSocket
CAsyncSocket
KBCategory: kbprg kbbuglist kbfixlist
KBSubcategory: MfcSockets
=============================================================================
Copyright Microsoft Corporation 1995.

********************************************************************************

Regards,
Gopal

Phil Carlisle (zoombapup)

unread,
Jan 24, 1997, 3:00:00 AM1/24/97
to


hay...@sierranet.net wrote in article
<32e3c0ca...@news.sierranet.net>...


> I'm having a problem with a little server I have been working on.
> Basically, my plan is to use a CAsyncSocket to be the listen socket
> that runs in the main thread of my Win32 program. I have overriden
> the OnAccept method of the CAsyncSocket class to accept the connection
> with a CSocket object and activate a new thread to handle the actual
> connection. Everything works fine until I try to close the CSocket
> object. I have tried code similar to the following:
>
> ConnectionThread::EndConnection()
> {
> m_pSocket->Close();
> delete m_pSocket;
> }
>

Sounds like the dreaded Thread local socket handles...

basically, m_Socket inside any CSocket or CAsyncSocket class is only valid
INSIDE the thread where the object is created, therefore you CANT just pass
CSockets etc around to other threads, you have to detach and reattach your
m_Socket's for the sockets value to be valid inside your new threads.

Phil.

0 new messages