Sockets FAQ/Tutorial

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Sockets FAQ/Tutorial

Post by Ryan Norton »

Chinese Version

At the wxForum we get asked a lot of wxSocket questions... so here's an attempt to answer some of these in a simple, coherent way.

If you have any comments, etc. please reply and I'll edit this post.
  1. Server Socket Creation

    In native apis like WINSOCK you use a construct called SOCKET for everything.... however wx seperates the duties of client sockets (sockets recieved from a server) and server sockets (sockets that recieve client sockets from other people).

    Code: Select all

    // Creates a wxSocketServer.... returning NULL on error
    // nPort is the port to connect to (80 for HTTP, etc.)
    wxSocketServer* wxCreateMyServerSocket(int nPort)
    {
        //
        //  Create an address for the port.
        //  This will only fail if you have a really obscure port number
        //
        wxIPV4address addr;
        if(!addr.Service(nPort))
            return NULL;
    
        //
        // Create a server socket for our address
        // (You check through Ok which we'll get to next)
        //
        wxSocketServer* pServerSocket = new wxSocketServer(addr);
    
        //
        // Could we create the server socket?
        //
        if (!pServerSocket->Ok())
        {
            //
            // Nope, print out a message if the port is in use
            // otherwise just return 
            //
            if (pServerSocket->LastError() == wxSOCKET_INVPORT)
                   wxMessageBox(wxT("Port in use!"));
            
            pServerSocket->Destroy();
            return NULL;
        }
    
        //
        //  Success... yah!
        //
        return pServerSocket;
    }
    
  2. wxSocketServer::Accept way of getting clients

    Just using Accept is normally not a good thing with GUI apps because Accept halts the main thread until a client is recieved. However, it works fine for console applications

    Code: Select all

    //
    // Normally, you go through a loop like this
    // bServeClients is a bool you define
    // when this is false it stops calling Accept
    //    
    while ( bServeClients )
    {
        //
        //  Here we call accept.
        //  As mentioned this will block until a client is recieved
        //
        wxSocketBase * pSocket = pServerSocket->Accept();
        
        //
        //  If we get a NULL socket it means something bad happened
        //
        if ( !pSocket )
            break;
    
        //
        //  This is something you define... is does whatever with
        //  the socket like reading/recieving and writing/sending
        //
        OnClientConnect(pSocket);
    
        
        //
        //  Destroy this client socket
        //
        pSocket->Destroy();
    }
    
    //
    //  Destroy the server socket
    //
    pServerSocket->Destroy();
    
  3. Event way of recieving clients

    This is the preferred way of recieving clients for GUI programs... note that because events are not supported with wxBase you cannot use this way with wxBase-only apps


    Hang on though - this is not a single-step process...
    1. Prepare and connect to events

      Code: Select all

      //
      // Sets up events for pServerSocket with the event handler
      // pHandler
      //
      void wxMySetupSocketEvents(wxSocketServer* pServerSocket,
                                 wxEvtHandler* pHandler)
      {
          // Make sure there's a valid event handler
          wxASSERT_MSG(pHandler != NULL || wxTheApp != NULL, 
                              wxT("NO EVENT HANDLER!"));
      
          //
          // We can use wxTheApp (aka &wxGetApp()) for events
          // if the handler is null
          //
          if (pHandler == NULL)
              pHandler = wxTheApp;
      
          //
          // Sets the event handler to recieve events
          //
          pServerSocket->SetEventHandler(*pHandler, ID_SERVER);
      
          //
          // Specify and connect the kind of events we want to recieve
          // Here we send server events to wxMyServer::OnServerEvent
          // and client events to wxMyServer::OnSocketEvent 
          //
          pHandler->Connect(ID_SERVER, wxEVT_SOCKET, 
              (wxObjectEventFunction) &wxMyServer::OnServerEvent );
          pHandler->Connect(ID_SOCKET, wxEVT_SOCKET, 
              (wxObjectEventFunction) &wxMyServer::OnSocketEvent );
      
          //
          // This can get involved but basically there
          // are several kinds of events we could recieve from
          // the server socket.... normally we just want connection
          // events
          //
          pServerSocket->SetNotify(wxSOCKET_CONNECTION_FLAG);
          
          //
          //  Tell the server we're ready to get events
          //  (Other classes don't require this, this is kind
          //   of a wxSockets-specific thing)
          //
          pServerSocket->Notify(true);
      }
      
    2. Server events (wxMyServer::OnServerEvent)

      Code: Select all

      void wxMyServer::OnServerEvent(class wxSocketEvent& evt)
      {
          // We only registered for connection events... make
          // sure we only got a connection event
          wxASSERT(evt.GetSocketEvent() == wxSOCKET_CONNECTION);
          
          //
          // Get our server socket
          //
          wxSocketServer* pServerSocket = (wxSocketServer*) evt.GetSocket();
          
          //
          // Call accept, but since we know we'll get a socket we'll
          // tell it not to block
          //
          wxSocketBase *  pSocket = pServerSocket->Accept(false);
      
          //
          // If a wierd fluke happens and we get a null socket break out
          //
          if(!pSocket)
              return;
      
          //
          // Setup for the client's events
          // We already called Connect for the server, so we don't
          // need to do it here
          //
          pSocket->SetEventHandler(*((wxEvtHandler*)this), ID_SOCKET);
          
          //
          // Recieve input events, which means the socket is ready
          // for reading writing etc..
          // Lost event means the person disconnected from our server
          // and we need to destroy the socket
          //
          pSocket->SetNotify(wxSOCKET_INPUT_FLAG | wxSOCKET_LOST_FLAG);
          
          //
          // We're ready to recieve client socket events
          //
          pSocket->Notify(true);
      }
      
    3. Client events (wxMyServer::OnClientEvent)

      Code: Select all

      void  wxMyServer::OnSocketEvent(class wxSocketEvent& evt)
      {
          //
          // Make sure we an event we registered for
          //
          wxASSERT(evt.GetSocketEvent() == wxSOCKET_INPUT ||
                   evt.GetSocketEvent() == wxSOCKET_LOST);
      
          //
          // Get our client socket
          //
          wxSocketBase* pSocket = evt.GetSocket();
      
          //
          // If we got an input event it means we 
          // can read/write to this client socket
          //
          if (evt.GetSocketEvent() == wxSOCKET_INPUT)
          {
              //
              //  Register only for a lost event
              //  We don't any more input events
              //
              pSocket->SetNotify(wxSOCKET_LOST_FLAG);
      
              //
              //  Just like in the Accept sample
              //  This is something you define that will read etc.
              //  from the client socket
              //
              OnClientConnect(pSocket);
          }
      
          //
          // We're done with this client socket...
          // Destroy it
          //
          pSocket->Destroy();
      }
      
    Note that with the Event version you eventually should call Destroy() or delete on the server socket when you're done with it.

  4. Fun with wxSocketBase::SetFlags

    Before you call Read or Write on the client socket you can call SetFlags to control how the wxSocket waits for data. There are 5 (!) possible flag combinations you can pass to SetFlags.

    wxSOCKET_NOWAIT

    This just means you're calling the raw send and recv socket functions without any special stuff - when you call wxSocketBase::Read or Write you are literally calling the raw native socket functions without any wrapper around it at all.

    wxSOCKET_NONE

    The default wxSocketBase flag. Some people assume does the same thing as wxSOCKET_NOWAIT, but actually there is a lot of wrapping going on here. First, an explanation.

    First, in unix/WINSOCK there is recv and send. These are basically fread and fwrite for sockets (or, alternatively, if you have not used C in a while, imagine them as wxFile::Read and wxFile::Write for wxSockets). When you call recv and send, you could get "WOULDBLOCK" error - this is sort of saying that the data isn't ready yet.

    Most (simpler) socket apps use function called select which blocks the thread until the data is ready - you can get this from wx too (wxSocketBase::GetError() == wxSOCKET_WOULDBLOCK) in which case you can ignore it and call read/write again.

    What wxSOCKET_NONE and a plain wxSOCKET_WAITALL do is they call select for a very limited amount of time, then either yield the thread or if its the main thread they process the GUI events, after which it checks to see if there is an error - if its the "WOULDBLOCK" error it ignores it, otherwise it returns on a real error. In the case of wxSOCKET_NONE it does those things over and over again until the timeout expires (wxSocketBase::SetTimeout) or the data size passed to read/write is recieved/sent. (Note that this means that wxSOCKET_NOWAIT ignores the timeout value).

    A word about calling this in the main thread. Basically it calls wxYield(). The problem is that if that is called again (i.e. a recursive yield) wxYield just returns false and won't process any events... thus leading to a blocking situation you truly want to avoid - so just be careful and make sure socket functoins and wherever you call wxYield is not called again after calling a socket function.

    wxSOCKET_BLOCK

    This is just like wxSOCKET_NONE except it does not yield the current thread, or in the case of the main thread process GUI events.

    See, that was simple, wasn't it? :)

    wxSOCKET_WAITALL

    A potentially dangerous flag. Imagine calling wxSOCKET_NONE in an infinite loop until all the data is read/written or an error occurs - that's essentailly what this flag does.

    As hinted, like wxSOCKET_NOWAIT this ignores the timeout value, but in a different way - it will not return until the size you passed through to read or write is recieved/sent or an error occurs!!! This can be bad because if the client computer is off by even a byte you're stuck in an infinite loop with no help at all.

    wxSOCKET_BLOCK | wxSOCKET_WAITALL

    This is the same as a plain wxSOCKET_WAITALL except that it doesn't yield the current thread or process events if on the main thread - in other words, imagine this as calling Read/Write with the wxSOCKET_BLOCK flag in an infinite loop until all the data is sent/recieved or an error occurs, as that's essentially what it is.

    Summary

    Generally, if you're careful to avoid the recursive wxYield problem I mentioned in wxSOCKET_NONE, you should never have to set the flags on the wxSocket, which is why I don't even mention them in the basic tutorial. If you want total control, use wxSOCKET_NOWAIT and do your own checking for the wxSOCKET_WOULDBLOCK error. Use wxSOCKET_BLOCK if you're using a console app. Use wxSOCKET_WAITALL, well, maybe if you are absolutely, 100% sure you're going to get all that data; combine wxSOCKET_BLOCK with wxSOCKET_WAITALL for the same case for blocking sockets.
  5. Using wxSockets with wxStreams

    Are wxSocketOutputStream and wxSocketInputStream. You pass a socket to them through their constructor and then you call Read and or Write like a normal wxStream. You can only Read/Write though, as Seeking/Length etc. won't work.

    The wxBook is rumored (well, by the person who wrote the socket chapter :)) to contain some cool things you can with wxSocketXXXStreams.
  6. Using wxSockets with wxThreads

    Using a thread for each client socket is pretty simple.... first we need to create our thread class -

    Code: Select all

    class wxMyServerThread : public wxThread
    {
        wxMyServer*   m_pServer;  //Our server
        wxSocketBase* m_pSocket;  //The client socket for read/write ops
    public:
        wxMyServerThread(wxMyServer* pServer, wxSocketBase* pSocket) : 
                m_pServer(pServer), m_pSocket(pSocket) {} 
        ~wxMyServerThread() { }
    
        virtual ExitCode Entry()
        {
            //
            //  Remember in the wxMyServer::OnSocketEvent
            //  in the above simple example?  This is the same thing!
            //  In here we read/write etc...  just remember that you're
            //  using threads now so be thread-safe :)
            //
            m_pServer->OnClientConnect(m_pSocket);
            
            //
            //  We're done with the client socket,
            //  so we destroy it here
            //
            m_pSocket->Destroy();
            return 0;
        }
    };
    

    Then we modify the wxMyServer::OnSocketEvent example we gave earlier to incorporate the thread -

    Code: Select all

    void  wxMyServer::OnSocketEvent(class wxSocketEvent& evt)
    {
        //
        // Make sure we an event we registered for
        //
        wxASSERT(evt.GetSocketEvent() == wxSOCKET_INPUT ||
                 evt.GetSocketEvent() == wxSOCKET_LOST);
    
        //
        // Get our client socket
        //
        wxSocketBase* pSocket = evt.GetSocket();
    
        //
        // If we got an input event it means we 
        // can read/write to this client socket
        //
        if (evt.GetSocketEvent() == wxSOCKET_INPUT)
        {
            //
            //  Register only for a lost event
            //  We don't any more input events
            //
            pSocket->SetNotify(wxSOCKET_LOST_FLAG);
    
            //
            //  Create and run our thread.
            //  We don't really need this wxMyServerThread as a member 
            //  variable since wxThread automatically
            //  deletes itself when it finishes - but you can make one if you want
            //        
            wxMyServerThread* pThread = 
                    new wxMyServerThread(this, pSocket);
            pThread->Create();
            pThread->Run();
        }
        else 
        {
            //
            // We're done with this client socket...
            // Destroy it... but we only do this on a
            // lost event - we do this at the end of the thread
            // we created earlier for input events
            //
            pSocket->Destroy();
        }
    }
    
    The main thing to remember with wxSockets and threads is that you need to remember to call wxSocketBase::Initialize before using sockets in a thread other then the main thread. Generally, it is convenient to do this in your derived wxApp class in the OnInit method:

    Code: Select all

    bool MyApp::OnInit()
    {
        //This call enables us to use wxSocket calls in secondary threads
        wxSocketBase::Initialize();
        
        //...
        return true;
    }
    
  7. Other useful references
    http://www.fortunecity.com/skyscraper/arpanet/6/cc.htm

    Old crash course in Unix sockets programming.

    http://www.sockets.com/winsock.htm

    Information and reference about the WINSOCK API.

    http://www.opengroup.org/onlinepubs/009 ... cket.h.htm

    Official reference for the Unix socket API.

That's it for now - have fun guys!
Last edited by Ryan Norton on Sun Feb 12, 2006 5:21 am, edited 14 times in total.
[Mostly retired moderator, still check in to clean up some stuff]
GianT
Earned some good credits
Earned some good credits
Posts: 124
Joined: Wed Mar 16, 2005 5:44 pm
Location: Guadeloupe, French West Indies
Contact:

Post by GianT »

Just a question, in the Server Socket Creation part, didn't you mean wxSocketServer instead of wxServerSocket ?

Code: Select all

// nPort is the port to connect to (80 for HTTP, etc.)
wxSocketServer* wxCreateMyServerSocket(int nPort)
{
    //
    //  Create an address for the port.
    //  This will only fail if you have a really obscure port number
    //
    wxIPV4address addr;
    if(!addr.Service(mPort))
        return NULL;

    //
    // Create a server socket for our address
    // (You check through Ok which we'll get to next)
    //
    wxSocketServer* pServerSocket = new wxSocketServer(addr);
Last edited by GianT on Mon Jun 20, 2005 5:28 am, edited 1 time in total.
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

GianT wrote:Just a question, in the Server Socket Creation part, didn't you mean wxSocketServer instead of wxServerSocket ?
// nPort is the port to connect to (80 for HTTP, etc.)
wxSocketServer* wxCreateMyServerSocket(int nPort)
{
//
// Create an address for the port.
// This will only fail if you have a really obscure port number
//
wxIPV4address addr;
if(!addr.Service(mPort))
return NULL;

//
// Create a server socket for our address
// (You check through Ok which we'll get to next)
//
wxSocketServer* pServerSocket = new wxSocketServer(addr);
Yeah thanks - it was edited after you posted... evidently :).
[Mostly retired moderator, still check in to clean up some stuff]
GianT
Earned some good credits
Earned some good credits
Posts: 124
Joined: Wed Mar 16, 2005 5:44 pm
Location: Guadeloupe, French West Indies
Contact:

Post by GianT »

Another mistake I think, in the function OnServerEvent, you wrote

Code: Select all

 wxSocketBase *  pSocket = pSocket->Accept(false);
I guess that the corect code is

Code: Select all

wxSocketBase *  pSocket = pServerSocket ->Accept(false);
And the last one, in the function OnSocketEvent, I think that you meant sock instead of pSocket:

Code: Select all

    if (evt.GetSocketEvent() == wxSOCKET_INPUT) 
    { 
        // 
        //  Register only for a lost event 
        //  We don't any more input events 
        // 
        pSocket->SetNotify(wxSOCKET_LOST_FLAG); 

        // 
        //  Just like in the Accept sample 
        //  This is something you define that will read etc. 
        //  from the client socket 
        // 
        OnClientConnect(pSocket); 
    } 
I think that this should work perfectly now :wink:
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

GianT wrote:Another mistake I think, in the function OnServerEvent, you wrote

Code: Select all

 wxSocketBase *  pSocket = pSocket->Accept(false);
I guess that the corect code is

Code: Select all

wxSocketBase *  pSocket = pServerSocket ->Accept(false);
And the last one, in the function OnSocketEvent, I think that you meant sock instead of pSocket:

Code: Select all

    if (evt.GetSocketEvent() == wxSOCKET_INPUT) 
    { 
        // 
        //  Register only for a lost event 
        //  We don't any more input events 
        // 
        pSocket->SetNotify(wxSOCKET_LOST_FLAG); 

        // 
        //  Just like in the Accept sample 
        //  This is something you define that will read etc. 
        //  from the client socket 
        // 
        OnClientConnect(pSocket); 
    } 
I think that this should work perfectly now :wink:
Thanks a bundle! Fixed.
[Mostly retired moderator, still check in to clean up some stuff]
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

Explanation of those pesky flags added
[Mostly retired moderator, still check in to clean up some stuff]
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

Added stream explanation and thread example
[Mostly retired moderator, still check in to clean up some stuff]
KevinHock
Filthy Rich wx Solver
Filthy Rich wx Solver
Posts: 236
Joined: Sat Sep 04, 2004 1:49 pm
Location: Ohio, USA
Contact:

Post by KevinHock »

Two comments:

1) You say there are 5 socket flags, but then describe only 4.

2) It's not clear at all that wxSOCKET_BLOCK is indepedent from all the others, i.e. you don't use it in isolation but in combination with one of the others.

Also, you may want to run through a spell check; Summary is misspelled.
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

KevinHock wrote:1) You say there are 5 socket flags, but then describe only 4.
Yes, actually I meant to say there were 5 possible socket flag combinations - thanks, corrected.

KevinHock wrote:2) It's not clear at all that wxSOCKET_BLOCK is indepedent from all the others, i.e. you don't use it in isolation but in combination with one of the others.
Yep, I made an attempt to fix this...
KevinHock wrote: Also, you may want to run through a spell check; Summary is misspelled.
Thanks, I don't have a spell checker at the moment, but I made a mental pass through most of it and I think I got most of them...
Last edited by Ryan Norton on Thu Jun 23, 2005 6:36 am, edited 1 time in total.
[Mostly retired moderator, still check in to clean up some stuff]
GianT
Earned some good credits
Earned some good credits
Posts: 124
Joined: Wed Mar 16, 2005 5:44 pm
Location: Guadeloupe, French West Indies
Contact:

Post by GianT »

Well, since eveyone understands the main idea, we don't care of spelling mistakes, it's already good having done and posted this tutorial, you are forgiven and thanked Ryan... :D But this makes me want to ask a question: are you paid for being moderators? (question to all moderators, not you particularly) Is it your job or are you just doing it while your spare time?? You can send me a private message if this is too indiscreet of me! ^^
Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg »

Let me just answer that I (as the guy that hosts, maintains, and administrates the forum) do not pay Ryan. :-) We are all volunteers and I am happy so many people are enthousiastic!

ps. Anybody interested in some moderation job can ask me. It basically means editing posts and moving posts and warning people about xposting.

Regards,
- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb
GianT
Earned some good credits
Earned some good credits
Posts: 124
Joined: Wed Mar 16, 2005 5:44 pm
Location: Guadeloupe, French West Indies
Contact:

Post by GianT »

Ok, it is noted. I will think about this after I have a real job and begin to earn my life :D
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

Kind-of-Offtopic warning.

First, my sincere apologies to Kevin [Hock] for the socket stream rant.

I wish him and Julian (and Stefan?) all the best with the wxBook.

Kind of irrelevant now but back when Julian was asking for submissions for the wxBook I really wanted to work on the thing, but given the insecurities I had I didn't have the courage to ask.

Anyway, I'm sure the wxBook will be a huge success...

(for those wondering Kevin wrote the sockets chapter in there. His baby is bitwise which is a pretty good example of how to use sockets, mine is WikiServer which is an ok example).
[Mostly retired moderator, still check in to clean up some stuff]
User avatar
Ryan Norton
wxWorld Domination!
wxWorld Domination!
Posts: 1319
Joined: Mon Aug 30, 2004 6:01 pm

Post by Ryan Norton »

GianT wrote:Ok, it is noted. I will think about this after I have a real job and begin to earn my life :D
Heh heh. I've never had a "real job" in this business, so you're not alone. Believe it or not, this is my hobby (well, wikiserver and wx is, which makes for an insanely time-consuming hobby :lol: ).
[Mostly retired moderator, still check in to clean up some stuff]
Jorg
Moderator
Moderator
Posts: 3971
Joined: Fri Aug 27, 2004 9:38 pm
Location: Delft, Netherlands
Contact:

Post by Jorg »

Yep, I also have no luxury of being able to program wxWidgets at work. I do this in my spare time (and at work when I feel like some distraction). At work I develop Delphi medical software. Soon we will migrate to Delphi 7 (finally) and then to C#.NET .. which will take a while but it will be a blast ..

As for C++ / wxWidgets, I have been programming that language ever since I started developing applications.

Regards,
- Jorgen
Forensic Software Engineer
Netherlands Forensic Insitute
http://english.forensischinstituut.nl/
-------------------------------------
Jorg's WasteBucket
http://www.xs4all.nl/~jorgb/wb
Post Reply