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.
-
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; }
-
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 applicationsCode: 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();
-
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...
- 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); }
- 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); }
- 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(); }
- Prepare and connect to events
-
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. -
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. -
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 -
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
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(); } }
Code: Select all
bool MyApp::OnInit() { //This call enables us to use wxSocket calls in secondary threads wxSocketBase::Initialize(); //... return true; }
-
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!