Re: CAsyncSocket and Threads
AFAIK microsoft's CAsyncServer example sucks.
See An MFC Asynchronous Socket Example Done Right
Re: CAsyncSocket and Threads
The CAsyncSocket class requires a message loop for successful operation. Thus, it cannot be used in a console application.
Re: CAsyncSocket and Threads
OK, so more experimentation here.
I'm writing a .dll that gets loaded into another program as a plug-in (think 3d app like Maya).
I've kicked off a thread using AfxBeginThread.
In that thread I want to connect to a server that is listening for messages like "hello" and "goodbye" and whatnot. I will write the server.
I'm using the server in the link above, the "An MFC Asynchronous Socket Example Done Right", as a test.
In the thread function I'm doing something very simple like I outlined above, either using CSocket or CAsyncSocket and neither is connecting (it errors on the Connect() command). So something as simple as:
Code:
AfxSocketInit();
CAsyncSocket c;
c.Create();
if( c.Connect( IP, PORT ) == 0 ) {
AfxMessageBox( "Crap!" );
return;
}
CString f = "hello";
c.Send( f, f.GetLength(), 0 );
c.ShutDown();
c.Close();
return;
doesn't work. It always craps on the connection portion.
The server app, however, says "Socket attached", so it's sorta connecting? I dunno. I guess I'm curious as to why even using CSocket (which blocks, yes, I know) wouldn't connect ...
Re: CAsyncSocket and Threads
On socket errors, you must call GetLastError() to determine the source of the error:
Code:
if( c.Connect( IP, PORT ) == 0 ) {
int iErr = GetLastError();
CString str;
str.Format(_T("Crap: GetLastError = %d"), iErr);
AfxMessageBox( str );
return;
}
Note that the most common error is completely expected and you must code for it: WSAEWOULDBLOCK (which is iErr = 10035). It simply means that Connect() could not complete right away, but it is expected to complete shortly at some indeterminate time in the future, and when it does complete, you will be notified by a call to your override of the OnConnect() virtual function. This mode of "try and wait for completion later" is typical of asynchronous sockets, and indeed is the precise reason why they are called "asynchronous".
Mike
Re: CAsyncSocket and Threads
Quote:
Originally Posted by
MikeAThon
On socket errors, you must call GetLastError() to determine the source of the error:
Code:
if( c.Connect( IP, PORT ) == 0 ) {
int iErr = GetLastError();
CString str;
str.Format(_T("Crap: GetLastError = %d"), iErr);
AfxMessageBox( str );
return;
}
Note that the most common error is completely expected and you must code for it: WSAEWOULDBLOCK (which is iErr = 10035). It simply means that Connect() could not complete right away, but it is expected to complete shortly at some indeterminate time in the future, and when it does complete, you will be notified by a call to your override of the OnConnect() virtual function. This mode of "try and wait for completion later" is typical of asynchronous sockets, and indeed is the precise reason why they are called "asynchronous".
Mike
Thanks Mike, I'll rewrite some code and give it a shot.
So I would need to do something like:
Code:
if( connect == 0 )
getErrorCode
if errorCode != WSAEWOULDBLOCK
crap! Connection dead!
then in my overridden OnConnect()
if( errorCode != 0 )
crap! No connection!
else
// if we're here, we're connected
Send( msg )
Re: CAsyncSocket and Threads
Yes, that's the basic idea.
Re: CAsyncSocket and Threads
So it's connecting but not sending.
in my OnConnect() function I have
if( errmsg != ERROR_SUCCESS )
// failed, do stuff, return failure
CString msg = "hi there!" );
Send( msg, msg.GetLength(), 0 ); // never received by server
ShutDown();
Close();
The server, again, using the "AsyncServer done right" app found here, http://www.flounder.com/kb192570.htm, says
Socket attached
???? [?] Closed
so, it's not even getting the send message, although I know it's getting to the OnConnect() as I've put various AfxMessageBoxes to show that it's in the OnConnect() function. Do I need to set up some loop like
while( sentmsg size > 0 )
send data
decrement sentmsg size by amount sent
Is that part of the whole asynchronous socket bit? I realize a regular 'ol CSocket would block, but using a CSocket instead of CAsyncSocket is resulting in the same thing - not connecting and not sending a message.
Re: CAsyncSocket and Threads
Again, for all socket functions, test the return value and if it's unexpected, call GetLastError() to find out what went wrong.
Do so on the Send() function, and tell us what GetLastError is returning.
Incidentally, CAsyncSocket is driven by Windows messages, so it generally is a bad idea to debug with AfxMessageBox, for the reason that the message box interferes with the message loop. Use TRACE statements instead.
Mike
Re: CAsyncSocket and Threads
Quote:
Originally Posted by
steebu
... Do I need to set up some loop like
while( sentmsg size > 0 )
send data
decrement sentmsg size by amount sent
Yes, that's correct, unless Send() fails for GetLastError == WSAEWOULDBLOCK. In this latter case, you must stop your attempts at sending, and should expect to resume sending in your OnSend handler, which will be called by the framework at some time in the future, when a call to Send is (in the words of the documentation) likely to succeed.
Re: CAsyncSocket and Threads
Quote:
Originally Posted by
MikeAThon
Yes, that's correct, unless Send() fails for GetLastError == WSAEWOULDBLOCK. In this latter case, you must stop your attempts at sending, and should expect to resume sending in your OnSend handler, which will be called by the framework at some time in the future, when a call to Send is (in the words of the documentation) likely to succeed.
OK, I think I'm just confusing myself because I'm not sure how this relates to that, etc. :-)
I have a subclass, mSocket, derived from CAsyncSocket.
When the user wants to send a message a function (execute()) gets called:
Code:
CWinThread* pThread;
pThread = AfxBeginThread( (AFX_THREADPROC)Show, (LPVOID)args[0].getStringValue(), THREAD_PRIORITY_NORMAL, 0, CREATE_SUSPENDED, NULL );
if( pThread == NULL ) {
logError( "couldn't start thread" );
return false;
}
pThread->ResumeThread();
in the thread function Show() I have
[code]
mSocket c;
c.msg = "hello";
c.Create();
if( c.Connect( LISTENER_IP, PORT ) == 0 ) {
int err = GetLastError();
CString str;
str.Format( _T("Show(): GetLastError = %d"), err );
return;
}
I then have
void mSocket::OnConnect( int error )
{
if( error != ERROR_SUCCESS ) {
// failed
AfxMessageBox( "Connection failed!" );
return;
}
CAsyncSocket::OnConnect( error );
AfxMessageBox( "Connection succeeded!" );
connected = 1;
bytessent = 0;
msgsize = buf.GetLength();
if( Send( buf, msgsize, 0 ) == SOCKET_ERROR ) {
int err = GetLastError();
CString str;
str.Format( _T("OnConnect(): GetLastError = %d"), err );
// display error message here
return;
}
ShutDown();
Close();
}
here I do get the WASEWOULDBLOCK error, so I guess that means it's getting to the send portion.
I have not overridden CAsyncSocket::Send(); I am using the base class's send() function. Do I need to override both Send() and OnSend()? What would I put in both?
I did do a test where I had overridden OnSend() and yanked the code from the MSDN OnSend() example:
void mSocket::OnSend( int error )
{
while( bytessent < msgsize ) {
int dwBytes;
if(( dwBytes = Send(( LPCTSTR)buf+bytessent,msgsize-bytessent)) == SOCKET_ERROR )
if( GetLastError() == WSAEWOULDBLOCK)
break;
else {
TCHAR szError[256];
_stprintf_s(szError, _T("Server Socket failed to send: %d"),
GetLastError());
Close();
// display error message here
}
else {
bytessent +=dwBytes;
if( bytessent == msgsize ) {
bytessent = msgsize = 0;
buf = _T("");
}
CAsyncSocket::OnSend( error );
}
}
Problem is, nothing still gets sent and the error message in the ouptut box of the AsyncServer is:
77c: ?.?.?.? [?] Closed
Re: CAsyncSocket and Threads
As pointed out in post#3, the CAsyncSocket class requires a message loop for successful operation. Thus, it cannot be used in a console application.
It also cannot be used in a worker thread, for the same reason, i.e., a worker thread does not have a message loop.
Why do you think you need a separate thread? One of the purposes of CAsyncSocket was to allow asynchronous operation within the context of the main GUI thread.
Mike
PS: You might be seeing some oddball behavior because of the use of a message box for debugging. A message box will run a message loop (the default modal loop inside of Windows) while it is displayed. But as mentioned in post #9 above, because CAsyncSocket is driven by Windows messages, it generally is a bad idea to debug with AfxMessageBox. Use TRACE statements instead.
Re: CAsyncSocket and Threads
Quote:
Originally Posted by
MikeAThon
As pointed out in post#3, the CAsyncSocket class requires a message loop for successful operation. Thus, it cannot be used in a console application.
It also cannot be used in a worker thread, for the same reason, i.e., a worker thread does not have a message loop.
Why do you think you need a separate thread? One of the purposes of CAsyncSocket was to allow asynchronous operation within the context of the main GUI thread.
Mike
PS: You might be seeing some oddball behavior because of the use of a message box for debugging. A message box will run a message loop (the default modal loop inside of Windows) while it is displayed. But as mentioned in post #9 above, because CAsyncSocket is driven by Windows messages, it generally is a bad idea to debug with AfxMessageBox. Use TRACE statements instead.
OK, will def take out the AfxMessageBox.
The "Better CAsyncSocket example" code kicks off a thread (albeit non-worker) because the user clicks "Connect" and has to get control back to the GUI. Since I'm building a plugin for a 3D package (like Maya), I figured I needed a thread to handle the message sending? Basically, I can build a native command for this 3d app that I can type into its script editor and have it do something, in this case, send a text message to a server on another machine listening to a particular tcp port.
Building a native command for this app goes something like this:
myNewCommand::execute( arg list )
{
// do stuff, like send messages
}
where the execute command is overridden from the Command class (class myNewCommand : public Command, etc.)
So, again, not knowing enough about asynchronous threads, inside of the execute function can I just do something like:
Code:
mySocket s; // derived from CAsyncSocket
s.Create();
s.Connect( ip, port );
if( // do error checking )
s.Send(); // write my own send
s.ShutDown();
s.Close();
Then I'd still have to write my own
Connect();
OnConnect();
Send()
OnSend();
replete with error handling?
Sorry for sounding like such a noob, but the doc and some examples I've seen are just so obtuse, or in some cases, completely wrong.
Re: CAsyncSocket and Threads
The use of CAsyncSocket in a separate thread is relatively uncommon, and is ordinarily completely unnecessary. It was for this reason that Dr. Newcomer wrote his article "An MFC Asynchronous Socket Example Done Right" ( http://www.flounder.com/kb192570.htm ):
Quote:
Originally Posted by An MFC Asynchronous Socket Example Done Right
There was a recent newsgroup posting about asynchronous sockets in separate threads...
One possible reason to use a separate thread involves servers that must accept many hundreds of connections. But ordinarily (and particularly for clients that establish only a few connections), there is absolutely no reason to use CAsyncSocket in a separate thread. And if you think you have a reason, then CAsyncSocket is probably the wrong architecture anyway (better to use IOCP).
Moreover, if you decide to use CAsyncSocket in a separate thread, the thread must be a UI thread (MFC nomenclature), which has its own message loop, as Dr. Newcomer's article makes abundantly clear.
That being said, the presence of a thread in your case seems unduly complex. I would re-write to keep everything in a single thread.
As for the functions that you need to write, you only need the OnXxx functions, like OnConnect and OnSend and OnReceive. Do NOT write your own Send() or Connect() functions.
Re: CAsyncSocket and Threads
OK, apologies for the denseness, but I feel like I'm back to square 1 with nothing accomplished.
The "entry" point for the SDK is the execute() function, so I've got
Code:
AfxSocketInit();
connected = 0;
s.Create();
if( s.Connect( args[0].getStringValue(), PORT ) == 0 ) {
int e = GetLastError();
logError( "Error code: %d", e );
if( e != WSAEWOULDBLOCK )
return false;
else
logInfo( "Waiting ..." ); // built-in SDK command to print output to the console
}
return true;
I've written my own OnConnect() function that simply says
Code:
CAsyncSocket::OnConnect( nErrorCode ); // saw this in Dr. NewComer's code - why do I have to do this?
Command::logInfo( "Connected!" ); // build-in command to send text output
ShutDown(); // yeah, yeah, not doing anything, just testing, that's all
Close();
The problem is, I never get a message that it was connected; that is, OnConnect() doesn't get called. I do get the output "Waiting ..." so it's definitely getting the WSAEWOULDBLOCK code, but ...
1. I never seem to get a connected notification. Or do I have to do something special to handle that?
2. what's the ordering of the statements? Can I just do:
Code:
mSocket s;
s.Create();
if( s.Connect( ip, port ) == 0 ) {
// error check
}
s.Send(); // so will the code wait to run this until it runs OnConnect(), which, seemingly never gets called?
The other thing, I guess, is why couldn't I run this with CSocket? I realize it blocks, but this is a simple single client, single server setup ... although, I tried rewriting this with CSocket and I'm getting a connect() error of 1, which isn't listed ...