X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/7fb0a11d3a06bb86eee92d71dffaf71918437c8c..35d5da677d3203d7027eb28b1b6878be9d920108:/src/common/socket.cpp diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 8e87adb874..a4f8583376 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -17,25 +17,30 @@ #include "wx/wxprec.h" #ifdef __BORLANDC__ -#pragma hdrstop + #pragma hdrstop #endif #if wxUSE_SOCKETS -#include "wx/app.h" -#include "wx/apptrait.h" -#include "wx/object.h" -#include "wx/string.h" -#include "wx/timer.h" -#include "wx/utils.h" -#include "wx/module.h" -#include "wx/log.h" -#include "wx/intl.h" -#include "wx/event.h" +#include "wx/socket.h" + +#ifndef WX_PRECOMP + #include "wx/object.h" + #include "wx/string.h" + #include "wx/intl.h" + #include "wx/log.h" + #include "wx/event.h" + #include "wx/app.h" + #include "wx/utils.h" + #include "wx/timer.h" + #include "wx/module.h" +#endif +#include "wx/apptrait.h" #include "wx/sckaddr.h" -#include "wx/socket.h" -#include "wx/datetime.h" +#include "wx/stopwatch.h" +#include "wx/thread.h" +#include "wx/evtloop.h" // DLL options compatibility check: #include "wx/build.h" @@ -97,6 +102,50 @@ public: DECLARE_NO_COPY_CLASS(wxSocketState) }; +// ============================================================================ +// GSocketManager +// ============================================================================ + +GSocketManager *GSocketManager::ms_manager = NULL; + +/* static */ +void GSocketManager::Set(GSocketManager *manager) +{ + wxASSERT_MSG( !ms_manager, "too late to set manager now" ); + + ms_manager = manager; +} + +/* static */ +void GSocketManager::Init() +{ + wxASSERT_MSG( !ms_manager, "shouldn't be initialized twice" ); + + /* + Details: Initialize() creates a hidden window as a sink for socket + events, such as 'read completed'. wxMSW has only one message loop + for the main thread. If Initialize is called in a secondary thread, + the socket window will be created for the secondary thread, but + since there is no message loop on this thread, it will never + receive events and all socket operations will time out. + BTW, the main thread must not be stopped using sleep or block + on a semaphore (a bad idea in any case) or socket operations + will time out. + + On the Mac side, Initialize() stores a pointer to the CFRunLoop for + the main thread. Because secondary threads do not have run loops, + adding event notifications to the "Current" loop would have no + effect at all, events would never fire. + */ + wxASSERT_MSG( wxIsMainThread(), + "sockets must be initialized from the main thread" ); + + wxAppConsole * const app = wxAppConsole::GetInstance(); + wxCHECK_RET( app, "sockets can't be initialized without wxApp" ); + + ms_manager = app->GetTraits()->GetSocketManager(); +} + // ========================================================================== // wxSocketBase // ========================================================================== @@ -118,31 +167,6 @@ bool wxSocketBase::Initialize() { if ( !m_countInit++ ) { - /* - Details: Initialize() creates a hidden window as a sink for socket - events, such as 'read completed'. wxMSW has only one message loop - for the main thread. If Initialize is called in a secondary thread, - the socket window will be created for the secondary thread, but - since there is no message loop on this thread, it will never - receive events and all socket operations will time out. - BTW, the main thread must not be stopped using sleep or block - on a semaphore (a bad idea in any case) or socket operations - will time out. - - On the Mac side, Initialize() stores a pointer to the CFRunLoop for - the main thread. Because secondary threads do not have run loops, - adding event notifications to the "Current" loop would have no - effect at all, events would never fire. - */ - wxASSERT_MSG( wxIsMainThread(), - wxT("Call wxSocketBase::Initialize() from the main thread first!")); - - wxAppTraits *traits = wxAppConsole::GetInstance() ? - wxAppConsole::GetInstance()->GetTraits() : NULL; - GSocketGUIFunctionsTable *functions = - traits ? traits->GetSocketGUIFunctionsTable() : NULL; - GSocket_SetGUIFunctions(functions); - if ( !GSocket_Init() ) { m_countInit--; @@ -157,7 +181,7 @@ bool wxSocketBase::Initialize() void wxSocketBase::Shutdown() { // we should be initialized - wxASSERT_MSG( m_countInit, _T("extra call to Shutdown()") ); + wxASSERT_MSG( m_countInit > 0, _T("extra call to Shutdown()") ); if ( --m_countInit == 0 ) { GSocket_Cleanup(); @@ -179,7 +203,8 @@ void wxSocketBase::Init() m_establishing = m_reading = m_writing = - m_error = false; + m_error = + m_closed = false; m_lcount = 0; m_timeout = 600; m_beingDeleted = false; @@ -315,8 +340,10 @@ wxSocketBase& wxSocketBase::Read(void* buffer, wxUint32 nbytes) return *this; } -wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes) +wxUint32 wxSocketBase::_Read(void* buffer_, wxUint32 nbytes) { + char *buffer = (char *)buffer_; + int total; // Try the pushback buffer first @@ -326,11 +353,9 @@ wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes) // Return now in one of the following cases: // - the socket is invalid, - // - we got all the data, - // - we got *some* data and we are not using wxSOCKET_WAITALL. + // - we got all the data if ( !m_socket || - !nbytes || - ((total != 0) && !(m_flags & wxSOCKET_WAITALL)) ) + !nbytes ) return total; // Possible combinations (they are checked in this order) @@ -343,35 +368,53 @@ wxUint32 wxSocketBase::_Read(void* buffer, wxUint32 nbytes) if (m_flags & wxSOCKET_NOWAIT) { m_socket->SetNonBlocking(1); - ret = m_socket->Read((char *)buffer, nbytes); + ret = m_socket->Read(buffer, nbytes); m_socket->SetNonBlocking(0); - if (ret > 0) - total += ret; + if ( ret < 0 ) + return 0; + + total += ret; } - else + else // blocking socket { - bool more = true; - - while (more) + for ( ;; ) { + // dispatch events unless disabled if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForRead() ) break; - ret = m_socket->Read((char *)buffer, nbytes); + ret = m_socket->Read(buffer, nbytes); + if ( ret == 0 ) + { + // for connection-oriented (e.g. TCP) sockets we can only read 0 + // bytes if the other end has been closed, and for connectionless + // ones (UDP) this flag doesn't make sense anyhow so we can set it to + // true too without doing any harm + m_closed = true; + break; + } - if (ret > 0) + if ( ret < 0 ) { - total += ret; - nbytes -= ret; - buffer = (char *)buffer + ret; + // this will be always interpreted as error by Read() + return 0; } - // If we got here and wxSOCKET_WAITALL is not set, we can leave - // now. Otherwise, wait until we recv all the data or until there - // is an error. - // - more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL)); + total += ret; + + // if wxSOCKET_WAITALL is not set, we can leave now as we did read + // something + if ( !(m_flags & wxSOCKET_WAITALL) ) + break; + + // otherwise check if read the maximal requested amount of data + nbytes -= ret; + if ( !nbytes ) + break; + + // we didn't, so continue reading + buffer = (char *)buffer + ret; } } @@ -516,8 +559,10 @@ wxSocketBase& wxSocketBase::Write(const void *buffer, wxUint32 nbytes) return *this; } -wxUint32 wxSocketBase::_Write(const void *buffer, wxUint32 nbytes) +wxUint32 wxSocketBase::_Write(const void *buffer_, wxUint32 nbytes) { + const char *buffer = (const char *)buffer_; + wxUint32 total = 0; // If the socket is invalid or parameters are ill, return immediately @@ -534,35 +579,42 @@ wxUint32 wxSocketBase::_Write(const void *buffer, wxUint32 nbytes) if (m_flags & wxSOCKET_NOWAIT) { m_socket->SetNonBlocking(1); - ret = m_socket->Write((const char *)buffer, nbytes); + ret = m_socket->Write(buffer, nbytes); m_socket->SetNonBlocking(0); if (ret > 0) total = ret; } - else + else // blocking socket { - bool more = true; - - while (more) + for ( ;; ) { if ( !(m_flags & wxSOCKET_BLOCK) && !WaitForWrite() ) break; - ret = m_socket->Write((const char *)buffer, nbytes); + ret = m_socket->Write(buffer, nbytes); + + // see comments for similar logic for ret handling in _Read() + if ( ret == 0 ) + { + m_closed = true; + break; + } - if (ret > 0) + if ( ret < 0 ) { - total += ret; - nbytes -= ret; - buffer = (const char *)buffer + ret; + return 0; } - // If we got here and wxSOCKET_WAITALL is not set, we can leave - // now. Otherwise, wait until we send all the data or until there - // is an error. - // - more = (ret > 0 && nbytes > 0 && (m_flags & wxSOCKET_WAITALL)); + total += ret; + if ( !(m_flags & wxSOCKET_WAITALL) ) + break; + + nbytes -= ret; + if ( !nbytes ) + break; + + buffer = (const char *)buffer + ret; } } @@ -678,7 +730,7 @@ bool wxSocketBase::_Wait(long seconds, wxSocketEventFlags flags) { GSocketEventFlags result; - long timeout; + long timeout; // in ms // Set this to true to interrupt ongoing waits m_interrupt = false; @@ -693,7 +745,9 @@ bool wxSocketBase::_Wait(long seconds, else timeout = m_timeout * 1000; - bool has_event_loop = wxTheApp ? (wxTheApp->GetTraits() ? true : false) : false; + // check if we are using event loop or not: normally we do in GUI but not in + // console applications but this can be overridden + const bool has_event_loop = wxEventLoop::GetActive() != NULL; // Wait in an active polling loop. // @@ -705,12 +759,11 @@ bool wxSocketBase::_Wait(long seconds, // Do this at least once (important if timeout == 0, when // we are just polling). Also, if just polling, do not yield. - wxDateTime current_time = wxDateTime::UNow(); - unsigned int time_limit = (current_time.GetTicks() * 1000) + current_time.GetMillisecond() + timeout; + const wxMilliClock_t time_limit = wxGetLocalTimeMillis() + timeout; bool done = false; bool valid_result = false; - if (!has_event_loop) + if (!has_event_loop) { // This is used to avoid a busy loop on wxBase - having a select // timeout of 50 ms per iteration should be enough. @@ -750,17 +803,16 @@ bool wxSocketBase::_Wait(long seconds, } // Wait more? - current_time = wxDateTime::UNow(); - int time_left = time_limit - ((current_time.GetTicks() * 1000) + current_time.GetMillisecond()); + long time_left = wxMilliClockToLong(time_limit - wxGetLocalTimeMillis()); if ((!timeout) || (time_left <= 0) || (m_interrupt)) done = true; else { - if (has_event_loop) + if (has_event_loop) { PROCESS_EVENTS(); } - else + else { // If there's less than 50 ms left, just call select with that timeout. if (time_left < 50) @@ -1009,6 +1061,8 @@ void wxSocketBase::OnRequest(wxSocketNotify notification) void wxSocketBase::Notify(bool notify) { m_notify = notify; + if (m_socket) + m_socket->Notify(notify); } void wxSocketBase::SetNotify(wxSocketEventFlags flags) @@ -1096,13 +1150,19 @@ wxSocketServer::wxSocketServer(const wxSockAddress& addr_man, return; } - // Setup the socket as server - + // Setup the socket as server + m_socket->Notify(m_notify); m_socket->SetLocal(addr_man.GetAddress()); if (GetFlags() & wxSOCKET_REUSEADDR) { m_socket->SetReusable(); } + if (GetFlags() & wxSOCKET_BROADCAST) { + m_socket->SetBroadcast(); + } + if (GetFlags() & wxSOCKET_NOBIND) { + m_socket->DontDoBind(); + } if (m_socket->SetServer() != GSOCK_NOERROR) { @@ -1117,6 +1177,8 @@ wxSocketServer::wxSocketServer(const wxSockAddress& addr_man, m_socket->SetCallback(GSOCK_INPUT_FLAG | GSOCK_OUTPUT_FLAG | GSOCK_LOST_FLAG | GSOCK_CONNECTION_FLAG, wx_socket_callback, (char *)this); + + wxLogTrace( wxTRACE_Socket, _T("wxSocketServer on fd %d"), m_socket->m_fd ); } // -------------------------------------------------------------------------- @@ -1193,7 +1255,7 @@ bool wxSocketBase::SetOption(int level, int optname, const void *optval, int optlen) { wxASSERT_MSG( m_socket, _T("Socket not initialised") ); - + if (m_socket->SetSockOpt(level, optname, optval, optlen) != GSOCK_NOERROR) { @@ -1202,7 +1264,7 @@ bool wxSocketBase::SetOption(int level, int optname, const void *optval, return true; } -bool wxSocketBase::SetLocal(wxIPV4address& local) +bool wxSocketBase::SetLocal(const wxIPV4address& local) { GAddress* la = local.GetAddress(); @@ -1228,6 +1290,8 @@ bool wxSocketBase::SetLocal(wxIPV4address& local) wxSocketClient::wxSocketClient(wxSocketFlags flags) : wxSocketBase(flags, wxSOCKET_CLIENT) { + m_initialRecvBufferSize = + m_initialSendBufferSize = -1; } wxSocketClient::~wxSocketClient() @@ -1238,7 +1302,9 @@ wxSocketClient::~wxSocketClient() // Connect // -------------------------------------------------------------------------- -bool wxSocketClient::DoConnect(wxSockAddress& addr_man, wxSockAddress* local, bool wait) +bool wxSocketClient::DoConnect(const wxSockAddress& addr_man, + const wxSockAddress* local, + bool wait) { GSocketError err; @@ -1273,6 +1339,14 @@ bool wxSocketClient::DoConnect(wxSockAddress& addr_man, wxSockAddress* local, bo { m_socket->SetReusable(); } + if (GetFlags() & wxSOCKET_BROADCAST) + { + m_socket->SetBroadcast(); + } + if (GetFlags() & wxSOCKET_NOBIND) + { + m_socket->DontDoBind(); + } // If no local address was passed and one has been set, use the one that was Set if (!local && m_localAddress.GetAddress()) @@ -1289,9 +1363,16 @@ bool wxSocketClient::DoConnect(wxSockAddress& addr_man, wxSockAddress* local, bo m_socket->SetLocal(la); } +#if defined(__WXMSW__) || defined(__WXGTK__) + m_socket->SetInitialSocketBuffers(m_initialRecvBufferSize, m_initialSendBufferSize); +#endif + m_socket->SetPeer(addr_man.GetAddress()); err = m_socket->Connect(GSOCK_STREAMED); + //this will register for callbacks - must be called after m_socket->m_fd was initialized + m_socket->Notify(m_notify); + if (!wait) m_socket->SetNonBlocking(0); @@ -1307,14 +1388,16 @@ bool wxSocketClient::DoConnect(wxSockAddress& addr_man, wxSockAddress* local, bo return true; } -bool wxSocketClient::Connect(wxSockAddress& addr_man, bool wait) +bool wxSocketClient::Connect(const wxSockAddress& addr_man, bool wait) { - return (DoConnect(addr_man, NULL, wait)); + return DoConnect(addr_man, NULL, wait); } -bool wxSocketClient::Connect(wxSockAddress& addr_man, wxSockAddress& local, bool wait) +bool wxSocketClient::Connect(const wxSockAddress& addr_man, + const wxSockAddress& local, + bool wait) { - return (DoConnect(addr_man, &local, wait)); + return DoConnect(addr_man, &local, wait); } bool wxSocketClient::WaitOnConnect(long seconds, long milliseconds) @@ -1342,14 +1425,27 @@ wxDatagramSocket::wxDatagramSocket( const wxSockAddress& addr, // Create the socket m_socket = GSocket_new(); - if(!m_socket) + if (!m_socket) { wxFAIL_MSG( _T("datagram socket not new'd") ); return; } + m_socket->Notify(m_notify); // Setup the socket as non connection oriented m_socket->SetLocal(addr.GetAddress()); - if( m_socket->SetNonOriented() != GSOCK_NOERROR ) + if (flags & wxSOCKET_REUSEADDR) + { + m_socket->SetReusable(); + } + if (GetFlags() & wxSOCKET_BROADCAST) + { + m_socket->SetBroadcast(); + } + if (GetFlags() & wxSOCKET_NOBIND) + { + m_socket->DontDoBind(); + } + if ( m_socket->SetNonOriented() != GSOCK_NOERROR ) { delete m_socket; m_socket = NULL; @@ -1379,7 +1475,7 @@ wxDatagramSocket& wxDatagramSocket::SendTo( const wxSockAddress& addr, wxUint32 nBytes ) { wxASSERT_MSG( m_socket, _T("Socket not initialised") ); - + m_socket->SetPeer(addr.GetAddress()); Write(buf, nBytes); return (*this);