diff --git a/docs/changes.txt b/docs/changes.txt index 5db8756baa..b732d87b69 100644 --- a/docs/changes.txt +++ b/docs/changes.txt @@ -529,6 +529,8 @@ Major new features in this release All: +- Add separate read/written bytes counters and per-direction NOWAIT and WAITALL + flags to wxSocket (Rob Bresalier). - Add wxDir::Close() method (Silverstorm82). - Fix compilation of wxHash{Map,Set} with g++ 4.7 (Nathan Ridge). - Fix posting large amounts of data in wxHTTP (Platonides). diff --git a/include/wx/socket.h b/include/wx/socket.h index 35370059e7..5181d3b475 100644 --- a/include/wx/socket.h +++ b/include/wx/socket.h @@ -77,7 +77,11 @@ enum wxSOCKET_BLOCK = 4, wxSOCKET_REUSEADDR = 8, wxSOCKET_BROADCAST = 16, - wxSOCKET_NOBIND = 32 + wxSOCKET_NOBIND = 32, + wxSOCKET_NOWAIT_READ = 64, + wxSOCKET_WAITALL_READ = 128, + wxSOCKET_NOWAIT_WRITE = 256, + wxSOCKET_WAITALL_WRITE = 512 }; typedef int wxSocketFlags; @@ -123,6 +127,8 @@ public: bool IsData() { return WaitForRead(0, 0); } bool IsDisconnected() const { return !IsConnected(); } wxUint32 LastCount() const { return m_lcount; } + wxUint32 LastReadCount() const { return m_lcount_read; } + wxUint32 LastWriteCount() const { return m_lcount_write; } wxSocketError LastError() const; void SaveState(); void RestoreState(); @@ -171,6 +177,8 @@ public: bool GetOption(int level, int optname, void *optval, int *optlen); bool SetOption(int level, int optname, const void *optval, int optlen); wxUint32 GetLastIOSize() const { return m_lcount; } + wxUint32 GetLastIOReadSize() const { return m_lcount_read; } + wxUint32 GetLastIOWriteSize() const { return m_lcount_write; } // event handling void *GetClientData() const { return m_clientData; } @@ -254,6 +262,8 @@ private: bool m_writing; // busy writing? bool m_closed; // was the other end closed? wxUint32 m_lcount; // last IO transaction size + wxUint32 m_lcount_read; // last IO transaction size of Read() direction. + wxUint32 m_lcount_write; // last IO transaction size of Write() direction. unsigned long m_timeout; // IO timeout value in seconds // (TODO: remove, wxSocketImpl has it too) wxList m_states; // stack of states (TODO: remove!) diff --git a/interface/wx/socket.h b/interface/wx/socket.h index 43d3c753c2..7e0d4ca014 100644 --- a/interface/wx/socket.h +++ b/interface/wx/socket.h @@ -572,7 +572,32 @@ enum wxSocketEventFlags in the output buffer. This is the same as issuing exactly one nonblocking low-level call to @b recv() or @b send(). Note that @e nonblocking here refers to when the function returns, not to whether the GUI blocks during - this time. + this time. Also note that this flag impacts both Read and Write + operations. If it is desired to control Read independently of Write, for + example you want no wait on Read(), but you do want to wait on Write(), then + use wxSOCKET_NOWAIT_READ and wxSOCKET_NOWAIT_WRITE. + + If @b wxSOCKET_NOWAIT_READ (this flag is new since wxWidgets 2.9.5) is + specified, Read operations will return immediately. Read operations will + retrieve only available data. This is the same as issuing exactly one + nonblocking low-level call to @b recv(). Note that @e nonblocking here + refers to when the function returns, not to whether the GUI blocks during + this time. This flag should not be enabled if ReadMsg() is going to be + used (it will be ignored), if you do then thread-safety may be at risk. + Note that wxSOCKET_NOWAIT_READ impacts only Read operations and does not + impact Write operations, allowing Read and Write operations to be set + differently. + + If @b wxSOCKET_NOWAIT_WRITE (this flag is new since wxWidgets 2.9.5) is + specified, Write operations will return immediately. Write operations will + write as much data as possible, depending on how much space is available in + the output buffer. This is the same as issuing exactly one nonblocking + low-level call to @b send(). Note that @e nonblocking here refers to when + the function returns, not to whether the GUI blocks during this time. This + flag should not be enabled if WriteMsg() is going to be used (it will be + ignored), if you use it then thread safety may be at risk. Note that + wxSOCKET_NOWAIT_WRITE impacts only Write operations and does not impact + Write operations, allowing Read and Write operations to be set differently. If @b wxSOCKET_WAITALL is specified, IO calls won't return until ALL the data has been read or written (or until an error occurs), blocking if @@ -580,7 +605,32 @@ enum wxSocketEventFlags same as having a loop which makes as many blocking low-level calls to @b recv() or @b send() as needed so as to transfer all the data. Note that @e blocking here refers to when the function returns, not - to whether the GUI blocks during this time. + to whether the GUI blocks during this time. Note that wxSOCKET_WAITALL + impacts both Read and Write operations. If you desire to wait + for all on just Read operations, but not on Write operations, (or vice versa), + use wxSOCKET_WAITALL_READ or wxSOCKET_WAITALL_WRITE. + + If @b wxSOCKET_WAITALL_READ (this flag is new since wxWidgets 2.9.5) is + specified, Read operations won't return until ALL the data has been read + (or until an error occurs), blocking if necessary, and issuing several low + level calls if necessary. This is the same as having a loop which makes as + many blocking low-level calls to @b recv() as needed so as to transfer all + the data. Note that @e blocking here refers to when the function returns, + not to whether the GUI blocks during this time. Note that + wxSOCKET_WAITALL_READ only has an impact on Read operations, and has no + impact on Write operations, allowing Read and Write operations to have + different settings. + + If @b wxSOCKET_WAITALL_WRITE (this flag is new since wxWidgets 2.9.5) is + specified, Write() and WriteMsg() calls won't return until ALL the data has + been written (or until an error occurs), blocking if necessary, and issuing + several low level calls if necessary. This is the same as having a loop + which makes as many blocking low-level calls to @b send() as needed so as + to transfer all the data. Note that @e blocking here refers to when the + function returns, not to whether the GUI blocks during this time. Note + that wxSOCKET_WAITALL_WRITE only has an impact on Write operations, and has + no impact on Read operations, allowing Read and Write operations to have + different settings. The @b wxSOCKET_BLOCK flag controls whether the GUI blocks during IO operations. If this flag is specified, the socket will not yield @@ -627,9 +677,13 @@ enum wxSOCKET_BLOCK = 4, ///< Block the GUI (do not yield) while reading/writing data. wxSOCKET_REUSEADDR = 8, ///< Allows the use of an in-use port. wxSOCKET_BROADCAST = 16, ///< Switches the socket to broadcast mode - wxSOCKET_NOBIND = 32 ///< Stops the socket from being bound to a specific + wxSOCKET_NOBIND = 32, ///< Stops the socket from being bound to a specific ///< adapter (normally used in conjunction with ///< @b wxSOCKET_BROADCAST) + wxSOCKET_NOWAIT_READ = 64, ///< Read as much data as possible and return immediately + wxSOCKET_WAITALL_READ = 128, ///< Wait for all required data to be read unless an error occurs. + wxSOCKET_NOWAIT_WRITE = 256, ///< Write as much data as possible and return immediately + wxSOCKET_WAITALL_WRITE = 512 ///< Wait for all required data to be written unless an error occurs. }; @@ -804,9 +858,42 @@ public: Use this function to get the number of bytes actually transferred after using one of the following IO calls: Discard(), Peek(), Read(), ReadMsg(), Unread(), Write(), WriteMsg(). + + @deprecated + This function is kept mostly for backwards compatibility. Use + LastReadCount() or LastWriteCount() instead. LastCount() is still + needed for use with less commonly used functions: Discard(), + Peek(), and Unread(). */ wxUint32 LastCount() const; + /** + Returns the number of bytes read by the last Read() or ReadMsg() + call (receive direction only). + + This function is thread-safe, in case Read() is executed in a + different thread than Write(). Use LastReadCount() instead of + LastCount() for this reason. + + Unlike LastCount(), the functions Discard(), Peek(), and Unread() + are currently not supported by LastReadCount(). + + @since 2.9.5 + */ + wxUint32 LastReadCount() const; + + /** + Returns the number of bytes written by the last Write() or WriteMsg() + call (transmit direction only). + + This function is thread-safe, in case Write() is executed in a + different thread than Read(). Use LastWriteCount() instead of + LastCount() for this reason. + + @since 2.9.5 + */ + wxUint32 LastWriteCount() const; + /** Returns the last wxSocket error. See @ref wxSocketError . @@ -936,7 +1023,7 @@ public: /** Read up to the given number of bytes from the socket. - Use LastCount() to verify the number of bytes actually read. + Use LastReadCount() to verify the number of bytes actually read. Use Error() to determine if the operation succeeded. @param buffer @@ -950,7 +1037,7 @@ public: The exact behaviour of Read() depends on the combination of flags being used. For a detailed explanation, see SetFlags() - @see Error(), LastError(), LastCount(), + @see Error(), LastError(), LastReadCount(), SetFlags() */ wxSocketBase& Read(void* buffer, wxUint32 nbytes); @@ -962,7 +1049,7 @@ public: bytes will be discarded. This function always waits for the buffer to be entirely filled, unless an error occurs. - Use LastCount() to verify the number of bytes actually read. + Use LastReadCount() to verify the number of bytes actually read. Use Error() to determine if the operation succeeded. @@ -978,8 +1065,15 @@ public: and it will always ignore the @b wxSOCKET_NOWAIT flag. The exact behaviour of ReadMsg() depends on the @b wxSOCKET_BLOCK flag. For a detailed explanation, see SetFlags(). + For thread safety, in case ReadMsg() and WriteMsg() are called in + different threads, it is a good idea to call + SetFlags(wxSOCKET_WAITALL|wx_SOCKET_BLOCK) before the first calls + to ReadMsg() and WriteMsg() in different threads, as each of these + functions will call SetFlags() which performs read/modify/write. By + setting these flags before the multi-threading, it will ensure that + they don't get reset by thread race conditions. - @see Error(), LastError(), LastCount(), SetFlags(), WriteMsg() + @see Error(), LastError(), LastReadCount(), SetFlags(), WriteMsg() */ wxSocketBase& ReadMsg(void* buffer, wxUint32 nbytes); @@ -1163,7 +1257,7 @@ public: /** Write up to the given number of bytes to the socket. - Use LastCount() to verify the number of bytes actually written. + Use LastWriteCount() to verify the number of bytes actually written. Use Error() to determine if the operation succeeded. @@ -1179,7 +1273,7 @@ public: The exact behaviour of Write() depends on the combination of flags being used. For a detailed explanation, see SetFlags(). - @see Error(), LastError(), LastCount(), SetFlags() + @see Error(), LastError(), LastWriteCount(), SetFlags() */ wxSocketBase& Write(const void* buffer, wxUint32 nbytes); @@ -1192,7 +1286,7 @@ public: This function always waits for the entire buffer to be sent, unless an error occurs. - Use LastCount() to verify the number of bytes actually written. + Use LastWriteCount() to verify the number of bytes actually written. Use Error() to determine if the operation succeeded. @@ -1209,8 +1303,15 @@ public: it will always ignore the @b wxSOCKET_NOWAIT flag. The exact behaviour of WriteMsg() depends on the @b wxSOCKET_BLOCK flag. For a detailed explanation, see SetFlags(). + For thread safety, in case ReadMsg() and WriteMsg() are called in + different threads, it is a good idea to call + @code SetFlags(wxSOCKET_WAITALL|wx_SOCKET_BLOCK) @endcode before the + first calls to ReadMsg() and WriteMsg() in different threads, as each + of these functions calls SetFlags() which performs read/modify/write. + By setting these flags before the multi-threading, it will ensure that + they don't get reset by thread race conditions. - @see Error(), LastError(), LastCount(), SetFlags(), ReadMsg() + @see Error(), LastError(), LastWriteCount(), SetFlags(), ReadMsg() */ wxSocketBase& WriteMsg(const void* buffer, wxUint32 nbytes); @@ -1321,7 +1422,7 @@ public: /** Write a buffer of @a nbytes bytes to the socket. - Use wxSocketBase::LastCount() to verify the number of bytes actually wrote. + Use wxSocketBase::LastWriteCount() to verify the number of bytes actually wrote. Use wxSocketBase::Error() to determine if the operation succeeded. @param address diff --git a/src/common/socket.cpp b/src/common/socket.cpp index 6f32e83791..78e4c049a6 100644 --- a/src/common/socket.cpp +++ b/src/common/socket.cpp @@ -817,6 +817,8 @@ void wxSocketBase::Init() m_writing = m_closed = false; m_lcount = 0; + m_lcount_read = 0; + m_lcount_write = 0; m_timeout = 600; m_beingDeleted = false; @@ -947,7 +949,8 @@ wxSocketBase& wxSocketBase::Read(void* buffer, wxUint32 nbytes) { wxSocketReadGuard read(this); - m_lcount = DoRead(buffer, nbytes); + m_lcount_read = DoRead(buffer, nbytes); + m_lcount = m_lcount_read; return *this; } @@ -983,7 +986,7 @@ wxUint32 wxSocketBase::DoRead(void* buffer_, wxUint32 nbytes) if ( m_impl->GetLastError() == wxSOCKET_WOULDBLOCK ) { // if we don't want to wait, just return immediately - if ( m_flags & wxSOCKET_NOWAIT ) + if ( m_flags & (wxSOCKET_NOWAIT|wxSOCKET_NOWAIT_READ )) { // this shouldn't be counted as an error in this case SetError(wxSOCKET_NOERROR); @@ -1019,7 +1022,7 @@ wxUint32 wxSocketBase::DoRead(void* buffer_, wxUint32 nbytes) // we're not going to read anything else and so if we haven't read // anything (or not everything in wxSOCKET_WAITALL case) already, // signal an error - if ( (m_flags & wxSOCKET_WAITALL) || !total ) + if ( (m_flags & (wxSOCKET_WAITALL|wxSOCKET_WAITALL_READ)) || !total ) SetError(wxSOCKET_IOERR); break; } @@ -1028,7 +1031,7 @@ wxUint32 wxSocketBase::DoRead(void* buffer_, wxUint32 nbytes) // if we are happy to read something and not the entire nbytes bytes, // then we're done - if ( !(m_flags & wxSOCKET_WAITALL) ) + if ( !(m_flags & (wxSOCKET_WAITALL|wxSOCKET_WAITALL_READ)) ) break; nbytes -= ret; @@ -1048,7 +1051,7 @@ wxSocketBase& wxSocketBase::ReadMsg(void* buffer, wxUint32 nbytes) wxSocketReadGuard read(this); - wxSocketWaitModeChanger changeFlags(this, wxSOCKET_WAITALL); + wxSocketWaitModeChanger changeFlags(this, (wxSOCKET_WAITALL|wxSOCKET_WAITALL_READ)); bool ok = false; if ( DoRead(&msg, sizeof(msg)) == sizeof(msg) ) @@ -1075,7 +1078,8 @@ wxSocketBase& wxSocketBase::ReadMsg(void* buffer, wxUint32 nbytes) len2 = 0; // Don't attempt to read if the msg was zero bytes long. - m_lcount = len ? DoRead(buffer, len) : 0; + m_lcount_read = len ? DoRead(buffer, len) : 0; + m_lcount = m_lcount_read; if ( len2 ) { @@ -1131,7 +1135,8 @@ wxSocketBase& wxSocketBase::Write(const void *buffer, wxUint32 nbytes) { wxSocketWriteGuard write(this); - m_lcount = DoWrite(buffer, nbytes); + m_lcount_write = DoWrite(buffer, nbytes); + m_lcount = m_lcount_write; return *this; } @@ -1151,7 +1156,7 @@ wxUint32 wxSocketBase::DoWrite(const void *buffer_, wxUint32 nbytes) { if ( m_impl->m_stream && !m_connected ) { - if ( (m_flags & wxSOCKET_WAITALL) || !total ) + if ( (m_flags & (wxSOCKET_WAITALL|wxSOCKET_WAITALL_WRITE)) || !total ) SetError(wxSOCKET_IOERR); break; } @@ -1161,7 +1166,7 @@ wxUint32 wxSocketBase::DoWrite(const void *buffer_, wxUint32 nbytes) { if ( m_impl->GetLastError() == wxSOCKET_WOULDBLOCK ) { - if ( m_flags & wxSOCKET_NOWAIT ) + if ( m_flags & (wxSOCKET_NOWAIT|wxSOCKET_NOWAIT_WRITE) ) break; if ( !DoWaitWithTimeout(wxSOCKET_OUTPUT_FLAG) ) @@ -1181,7 +1186,7 @@ wxUint32 wxSocketBase::DoWrite(const void *buffer_, wxUint32 nbytes) total += ret; - if ( !(m_flags & wxSOCKET_WAITALL) ) + if ( !(m_flags & (wxSOCKET_WAITALL|wxSOCKET_WAITALL_WRITE)) ) break; nbytes -= ret; @@ -1201,7 +1206,7 @@ wxSocketBase& wxSocketBase::WriteMsg(const void *buffer, wxUint32 nbytes) wxSocketWriteGuard write(this); - wxSocketWaitModeChanger changeFlags(this, wxSOCKET_WAITALL); + wxSocketWaitModeChanger changeFlags(this, (wxSOCKET_WAITALL|wxSOCKET_WAITALL_WRITE)); msg.sig[0] = (unsigned char) 0xad; msg.sig[1] = (unsigned char) 0xde; @@ -1216,8 +1221,9 @@ wxSocketBase& wxSocketBase::WriteMsg(const void *buffer, wxUint32 nbytes) bool ok = false; if ( DoWrite(&msg, sizeof(msg)) == sizeof(msg) ) { - m_lcount = DoWrite(buffer, nbytes); - if ( m_lcount == nbytes ) + m_lcount_write = DoWrite(buffer, nbytes); + m_lcount = m_lcount_write; + if ( m_lcount_write == nbytes ) { msg.sig[0] = (unsigned char) 0xed; msg.sig[1] = (unsigned char) 0xfe; diff --git a/tests/net/socket.cpp b/tests/net/socket.cpp index aece666280..3532618f8c 100644 --- a/tests/net/socket.cpp +++ b/tests/net/socket.cpp @@ -186,6 +186,7 @@ void SocketTestCase::ReadNormal() CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() ); CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastCount() ); + CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastReadCount() ); char bufBig[102400]; @@ -193,6 +194,7 @@ void SocketTestCase::ReadNormal() CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() ); CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastCount() ); + CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastReadCount() ); } void SocketTestCase::ReadBlock() @@ -206,6 +208,7 @@ void SocketTestCase::ReadBlock() CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() ); CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastCount() ); + CPPUNIT_ASSERT_EQUAL( WXSIZEOF(bufSmall), (size_t)sock->LastReadCount() ); char bufBig[102400]; @@ -213,6 +216,7 @@ void SocketTestCase::ReadBlock() CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() ); CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastCount() ); + CPPUNIT_ASSERT( WXSIZEOF(bufBig) >= sock->LastReadCount() ); } void SocketTestCase::ReadNowait() @@ -242,6 +246,7 @@ void SocketTestCase::ReadWaitall() CPPUNIT_ASSERT_EQUAL( wxSOCKET_NOERROR, sock->LastError() ); CPPUNIT_ASSERT_EQUAL( WXSIZEOF(buf), (size_t)sock->LastCount() ); + CPPUNIT_ASSERT_EQUAL( WXSIZEOF(buf), (size_t)sock->LastReadCount() ); } void SocketTestCase::UrlTest()