CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Page 1 of 2 12 LastLast
Results 1 to 15 of 19
  1. #1
    Join Date
    Jun 2008
    Posts
    12

    CAsyncSocket and Threads

    Hi,

    I've browsed lots of resources on the topic and looked at lots of code, but I'm having a bit of a noob moment here trying to figure something out.

    I've downloaded microsoft's CAsyncServer example here:
    http://download.microsoft.com/downlo...S/MFCAsync.exe

    It's an MFC-based client and server app, where the server listens to port 9898 and the client can connect to the server by providing the IP address. The client can send messages to the server which the server can display. Pretty straightforward stuff.

    I tried writing my own console-based program to be able to communicate with the server but am having no luck.

    1. The server is subclassing CAsyncSocket. Does my code have to use CAsyncSocket to connect as well, or can I use CSocket? Yes, I'm aware that CSocket blocks; I'm just asking. :-) That is, is there a 1-to-1 mapping between the server and client as to whether you can use CSocket vs. CAsyncSocket?

    2. My code, regardless of using CSocket or CAsyncSocket doesn't connect. Here's the code:


    Code:
    // TCPTest.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "TCPTest.h"
    #include <afxsock.h>
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    
    // The one and only application object
    
    CWinApp theApp;
    
    using namespace std;
    
    int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
    {
    	int nRetCode = 0;
    
    
    	// initialize MFC and print and error on failure
    	if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    	{
    		// TODO: change error code to suit your needs
    		_tprintf(_T("Fatal Error: MFC initialization failed\n"));
    		nRetCode = 1;
    	}
    	else
    	{
    		// Initialize the AfxSocket
    		if( !(AfxSocketInit(NULL))) {
    			cout << "Cannot initialize socket!" << endl;
    			return false;
    		}
    
    
    		CSocket connection;
    		if( connection == NULL ) {
    			cout << "crap!" << endl;
    			return false;
    		}
    		
    		cout << "Creating connection" << endl;
    		// create the connection
    		if( !connection.Create() ) {
    			cout << "Error: Cannot create socket!" << endl;
    			return false;
    		}
    
    		// try to connect it to the listener
    		if( !connection.Connect( (LPCTSTR)"10.100.20.8", 9898 )) {
    			LPVOID lpMsgBuf;
    
    			DWORD dw = GetLastError(); 
    
    			FormatMessage(
    				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    				FORMAT_MESSAGE_FROM_SYSTEM |
    				FORMAT_MESSAGE_IGNORE_INSERTS,
    				NULL, dw,
    				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    				(LPTSTR) &lpMsgBuf,	0, NULL );
    
    			CString msg = (LPTSTR)lpMsgBuf;
    			cout << "Error: Cannot connect to listener, maybe server not running?" << endl;
    			connection.ShutDown( 2 );
    			return false;
    		}
    
    
    			CString cmd;
    			cmd = "hi there";
    			connection.Send( cmd, cmd.GetLength(), 0 );
    			cout << "done!";
    			
    		// close the connection
    		//connection.ShutDown(2);
    
    		connection.Close();
    		//delete connection;
    		}
    
    		return nRetCode;
    }

    The app successfully gets to the connect part, but hangs, then throws the "Error: Cannot connect to listener, maybe server not running?" message.

    I am running this app on my laptop. The server is running on my desktop. I know there's no firewall issue as I can run the client app (the one that comes with the server app) on my laptop and can connect and send messages back and forth.

    So basically, I've looked at other examples, and all I really need to do is

    AfxSocketInit()
    create a Socket/ASyncSocket
    run Socket.Create()
    Socket.Connect( server, port )
    Socket.Send( msg, msg.GetLength() )
    Socket.Close()

    That's essentially what's happening above, but it just doesn't connect! Bah!

  2. #2
    VictorN's Avatar
    VictorN is offline Super Moderator Power Poster
    Join Date
    Jan 2003
    Location
    Hanover Germany
    Posts
    20,395

    Re: CAsyncSocket and Threads

    AFAIK microsoft's CAsyncServer example sucks.
    See An MFC Asynchronous Socket Example Done Right
    Victor Nijegorodov

  3. #3
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: CAsyncSocket and Threads

    The CAsyncSocket class requires a message loop for successful operation. Thus, it cannot be used in a console application.

  4. #4
    Join Date
    Jun 2008
    Posts
    12

    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 ...

  5. #5
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    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 = &#37;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

  6. #6
    Join Date
    Jun 2008
    Posts
    12

    Re: CAsyncSocket and Threads

    Quote Originally Posted by MikeAThon View Post
    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 )

  7. #7
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: CAsyncSocket and Threads

    Yes, that's the basic idea.

  8. #8
    Join Date
    Jun 2008
    Posts
    12

    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.

  9. #9
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    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

  10. #10
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    Re: CAsyncSocket and Threads

    Quote Originally Posted by steebu View Post
    ... 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.

  11. #11
    Join Date
    Jun 2008
    Posts
    12

    Re: CAsyncSocket and Threads

    Quote Originally Posted by MikeAThon View Post
    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

  12. #12
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    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.

  13. #13
    Join Date
    Jun 2008
    Posts
    12

    Re: CAsyncSocket and Threads

    Quote Originally Posted by MikeAThon View Post
    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.

  14. #14
    Join Date
    Nov 2002
    Location
    California
    Posts
    4,556

    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.
    Last edited by MikeAThon; May 3rd, 2010 at 06:20 PM.

  15. #15
    Join Date
    Jun 2008
    Posts
    12

    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: &#37;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 ...

Page 1 of 2 12 LastLast

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured