]> git.donarmstrong.com Git - bamtools.git/commitdiff
Basic Windows implementation
authorderek <derekwbarnett@gmail.com>
Thu, 17 Nov 2011 02:02:26 +0000 (21:02 -0500)
committerderek <derekwbarnett@gmail.com>
Thu, 17 Nov 2011 02:02:26 +0000 (21:02 -0500)
src/api/internal/io/TcpSocketEngine_unix_p.cpp
src/api/internal/io/TcpSocketEngine_win_p.cpp

index 35d81605771f9ded8807b781be6cc2cc4f3b0c8c..efcdf8d92ac3046ee48aaf6c3c649377d3ed13fe 100644 (file)
@@ -2,7 +2,7 @@
 // TcpSocketEngine_unix_p.cpp (c) 2011 Derek Barnett
 // Marth Lab, Department of Biology, Boston College
 // ---------------------------------------------------------------------------
-// Last modified: 10 November 2011 (DB)
+// Last modified: 15 November 2011 (DB)
 // ---------------------------------------------------------------------------
 // Provides low-level implementation of TCP I/O for all UNIX-like systems
 // ***************************************************************************
@@ -24,31 +24,6 @@ using namespace std;
 namespace BamTools {
 namespace Internal {
 
-//static inline
-//void getPortAndAddress(const sockaddr* s, uint16_t& port, HostAddress& address) {
-
-//    // IPv6
-//    if (s->sa_family == AF_INET6) {
-//        sockaddr_in6* ip6 = (sockaddr_in6*)s;
-//        port = ntohs(ip6->sin6_port);
-//        IPv6Address tmp;
-//        memcpy(&tmp.data, &(ip6->sin6_addr.s6_addr), sizeof(tmp));
-//        address.SetAddress(tmp);
-//        return;
-//    }
-
-//    // IPv4
-//    if ( s->sa_family == AF_INET ) {
-//        sockaddr_in* ip4 = (sockaddr_in*)s;
-//        port = ntohl(ip4->sin_port);
-//        address.SetAddress( ntohl(ip4->sin_addr.s_addr) );
-//        return;
-//    }
-
-//    // should be unreachable
-//    BT_ASSERT_X(false, "TcpSocketEngine::getPortAndAddress() : unknown network protocol ");
-//}
-
 } // namespace Internal
 } // namespace BamTools
 
@@ -100,19 +75,21 @@ bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t p
     // attempt connection
     int connectResult = connect(m_socketDescriptor, sockAddrPtr, sockAddrSize);
 
-    // if hit error
+    // if failed, handle error
     if ( connectResult == -1 ) {
 
-        // see what error was encountered
-        switch ( errno ) {
+        // ensure state is set before checking errno
+        m_socketState = TcpSocket::UnconnectedState;
+
+        // set error type/message depending on errno
+        switch ( errno ) { // <-- potential thread issues later? but can't get error type from connectResult
 
             case EISCONN:
-                m_socketState = TcpSocket::ConnectedState;
+                m_socketState = TcpSocket::ConnectedState; // socket was already connected
                 break;
             case ECONNREFUSED:
             case EINVAL:
                 m_socketError = TcpSocket::ConnectionRefusedError;
-                m_socketState = TcpSocket::UnconnectedState;
                 m_errorString = "connection refused";
                 break;
             case ETIMEDOUT:
@@ -121,32 +98,26 @@ bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t p
                 break;
             case EHOSTUNREACH:
                 m_socketError = TcpSocket::NetworkError;
-                m_socketState = TcpSocket::UnconnectedState;
                 m_errorString = "host unreachable";
                 break;
             case ENETUNREACH:
                 m_socketError = TcpSocket::NetworkError;
-                m_socketState = TcpSocket::UnconnectedState;
                 m_errorString = "network unreachable";
                 break;
             case EADDRINUSE:
-                m_socketError = TcpSocket::NetworkError;
+                m_socketError = TcpSocket::SocketResourceError;
                 m_errorString = "address already in use";
                 break;
             case EACCES:
             case EPERM:
                 m_socketError = TcpSocket::SocketAccessError;
-                m_socketState = TcpSocket::UnconnectedState;
                 m_errorString = "permission denied";
-            case EAFNOSUPPORT:
-            case EBADF:
-            case EFAULT:
-            case ENOTSOCK:
-                m_socketState = TcpSocket::UnconnectedState;
+                break;
             default:
                 break;
         }
 
+        // double check that we're not in 'connected' state; if so, return failure
         if ( m_socketState != TcpSocket::ConnectedState )
             return false;
     }
@@ -201,39 +172,6 @@ bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol)
     return true;
 }
 
-//bool TcpSocketEngine::nativeFetchConnectionParameters(void) {
-
-//    // reset addresses/ports
-////    m_localAddress.Clear();
-//    m_remoteAddress.Clear();
-////    m_localPort  = 0;
-//    m_remotePort = 0;
-
-//    // skip (return failure) if invalid socket FD
-//    if ( m_socketDescriptor == -1 )
-//        return false;
-
-//    sockaddr sa;
-//    BT_SOCKLEN_T sockAddrSize = sizeof(sa);
-
-//    // fetch local address info
-//    memset(&sa, 0, sizeof(sa));
-//    if ( getsockname(m_socketDescriptor, &sa, &sockAddrSize) == 0 )
-//        getPortAndAddress(&sa, m_localPort, m_localAddress);
-//    else if ( errno == EBADF ) {
-//        m_socketError = TcpSocket::UnsupportedSocketOperationError;
-//        m_errorString = "invalid socket descriptor";
-//        return false;
-//    }
-
-//    // fetch remote address
-//    if ( getpeername(m_socketDescriptor, &sa, &sockAddrSize) == 0 )
-//        getPortAndAddress(&sa, m_remotePort, m_remoteAddress);
-
-//    // return success
-//    return true;
-//}
-
 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
 
     // fetch number of bytes, return 0 on error
@@ -280,12 +218,10 @@ int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
     tv.tv_usec = (msecs % 1000) * 1000;
 
     // do 'select'
-    int ret;
     if ( isRead )
-        ret = select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
+        return select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
     else
-        ret = select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
-    return ret;
+        return select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
 }
 
 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
index cb3ddca681a264d9c166d05dd8f2bea2945c0d17..d1691aca72dd874585680e099c6c5272b2e69c76 100644 (file)
@@ -2,7 +2,7 @@
 // TcpSocketEngine_win_p.cpp (c) 2011 Derek Barnett
 // Marth Lab, Department of Biology, Boston College
 // ---------------------------------------------------------------------------
-// Last modified: 10 November 2011 (DB)
+// Last modified: 15 November 2011 (DB)
 // ---------------------------------------------------------------------------
 // Provides low-level implementation of TCP I/O for all Windows systems
 // ***************************************************************************
@@ -12,6 +12,7 @@
 using namespace BamTools;
 using namespace BamTools::Internal;
 
+#include <cstring>
 #include <iostream>
 using namespace std;
 
@@ -22,31 +23,6 @@ using namespace std;
 namespace BamTools {
 namespace Internal {
 
-//static inline
-//void getPortAndAddress(const sockaddr* s, uint16_t& port, HostAddress& address) {
-
-//    // IPv6
-//    if (s->sa_family == AF_INET6) {
-//        sockaddr_in6* ip6 = (sockaddr_in6*)s;
-//        port = ntohs(ip6->sin6_port);
-//        IPv6Address tmp;
-//        memcpy(&tmp, &ip6->sin6_addr.in6_addr, sizeof(tmp));
-//        address.SetAddress(tmp);
-//        return;
-//    }
-
-//    // IPv4
-//    if ( s->sa_family == AF_INET ) {
-//        sockaddr_in* ip4 = (sockaddr_in*)s;
-//        port = ntohl(ip4->sin_port);
-//        address.SetAddress( ntohl(ip4->sin_addr) );
-//        return;
-//    }
-
-//    // should be unreachable
-//    BT_ASSERT_X(false, "TcpSocketEngine::getPortAndAddress() : unknown network protocol ");
-//    return false;
-//}
 
 } // namespace Internal
 } // namespace BamTools
@@ -56,258 +32,244 @@ namespace Internal {
 // --------------------------------
 
 void TcpSocketEngine::nativeClose(void) {
-
-//    close(m_socketDescriptor);
+    closesocket(m_socketDescriptor);
 }
 
 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
 
     // setup connection parameters from address/port
-//    sockaddr_in  sockAddrIPv4;
-//    sockaddr_in6 sockAddrIPv6;
-//    sockaddr*    sockAddrPtr  = 0;
-//    BT_SOCKLEN_T sockAddrSize = 0;
-
-//    // IPv6
-//    if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
-
-//        memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
-//        sockAddrIPv6.sin6_family = AF_INET6;
-//        sockAddrIPv6.sin6_port   = htons(port);
-
-//        IPv6Address ip6 = address.GetIPv6Address();
-//        memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
-
-//        sockAddrSize = sizeof(sockAddrIPv6);
-//        sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
-//    }
-
-//    // IPv4
-//    else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
-
-//        memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
-//        sockAddrIPv4.sin_family      = AF_INET;
-//        sockAddrIPv4.sin_port        = htons(port);
-//        sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
-
-//        sockAddrSize = sizeof(sockAddrIPv4);
-//        sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
-//    }
-
-//    // unknown (should be unreachable)
-//    else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
-
-//    // attempt conenction
-//    int connectResult = connect(socketDescriptor, sockAddrPtr, sockAddrSize);
-
-//    // if hit error
-//    if ( connectResult == -1 ) {
-
-//        // see what error was encountered
-//        switch ( errno ) {
-
-//            case EISCONN:
-//                m_socketState = TcpSocket::ConnectedState;
-//                break;
-//            case ECONNREFUSED:
-//            case EINVAL:
-//                m_socketError = TcpSocket::ConnectionRefusedError;
-//                m_socketState = TcpSocket::UnconnectedState;
-//                m_errorString = "connection refused";
-//                break;
-//            case ETIMEDOUT:
-//                m_socketError = TcpSocket::NetworkError;
-//                m_errorString = "connection timed out";
-//                break;
-//            case EHOSTUNREACH:
-//                m_socketError = TcpSocket::NetworkError;
-//                m_socketState = TcpSocket::UnconnectedState;
-//                m_errorString = "host unreachable";
-//                break;
-//            case ENETUNREACH:
-//                m_socketError = TcpSocket::NetworkError;
-//                m_socketState = TcpSocket::UnconnectedState;
-//                m_errorString = "network unreachable";
-//                break;
-//            case EADDRINUSE:
-//                m_socketError = TcpSocket::NetworkError;
-//                m_errorString = "address already in use";
-//                break;
-//            case EACCES:
-//            case EPERM:
-//                m_socketError = TcpSocket::SocketAccessError;
-//                m_socketState = TcpSocket::UnconnectedState;
-//                m_errorString = "permission denied";
-//            case EAFNOSUPPORT:
-//            case EBADF:
-//            case EFAULT:
-//            case ENOTSOCK:
-//                m_socketState = TcpSocket::UnconnectedState;
-//            default:
-//                break;
-//        }
-
-//        if ( m_socketState != TcpSocket::ConnectedState )
-//            return false;
-//    }
-
-//    // otherwise, we should be good
-//    // update state & return success
-//    m_socketState = TcpSocket::ConnectedState;
-//    return true;
+    sockaddr_in  sockAddrIPv4;
+    sockaddr_in6 sockAddrIPv6;
+    sockaddr*    sockAddrPtr  = 0;
+    BT_SOCKLEN_T sockAddrSize = 0;
+
+    // IPv6
+    if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
+
+        memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
+        sockAddrIPv6.sin6_family = AF_INET6;
+        sockAddrIPv6.sin6_port   = htons(port);
+
+        IPv6Address ip6 = address.GetIPv6Address();
+        memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
+
+        sockAddrSize = sizeof(sockAddrIPv6);
+        sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
+    }
+
+    // IPv4
+    else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
+
+        memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
+        sockAddrIPv4.sin_family      = AF_INET;
+        sockAddrIPv4.sin_port        = htons(port);
+        sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
+
+        sockAddrSize = sizeof(sockAddrIPv4);
+        sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
+    }
+
+    // unknown (should be unreachable)
+    else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
+
+    // attempt conenction
+    const int connectResult = WSAConnect(m_socketDescriptor, sockAddrPtr, sockAddrSize, 0, 0, 0, 0);
+
+    // if failed, handle error
+    if ( connectResult == SOCKET_ERROR ) {
+
+        // ensure state is set before checking error code
+        m_socketState = TcpSocket::UnconnectedState;
+
+        // set error type/message depending on errorCode
+        const int errorCode = WSAGetLastError();
+        switch ( errorCode ) {
+            case WSANOTINITIALISED:
+                m_socketError = TcpSocket::UnknownSocketError;
+                m_errorString = "Windows socket functionality not properly initialized";
+                break;
+            case WSAEISCONN:
+                m_socketState = TcpSocket::ConnectedState; // socket already connected
+                break;
+            case WSAECONNREFUSED:
+            case WSAEINVAL:
+                m_socketError = TcpSocket::ConnectionRefusedError;
+                m_errorString = "connection refused";
+                break;
+            case WSAETIMEDOUT:
+                m_socketError = TcpSocket::NetworkError;
+                m_errorString = "connection timed out";
+                break;
+            case WSAEHOSTUNREACH:
+                m_socketError = TcpSocket::NetworkError;
+                m_errorString = "host unreachable";
+                break;
+            case WSAENETUNREACH:
+                m_socketError = TcpSocket::NetworkError;
+                m_errorString = "network unreachable";
+                break;
+            case WSAEADDRINUSE:
+                m_socketError = TcpSocket::SocketResourceError;
+                m_errorString = "address already in use";
+                break;
+            case WSAEACCES:
+                m_socketError = TcpSocket::SocketAccessError;
+                m_errorString = "permission denied";
+                break;
+            default:
+                break;
+        }
+
+        // double check that we're not in 'connected' state; if so, return failure
+        if ( m_socketState != TcpSocket::ConnectedState )
+            return false;
+    }
+
+    // otherwise, we should be good
+    // update state & return success
+    m_socketState = TcpSocket::ConnectedState;
+    return true;
 }
 
 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
 
-//    // get protocol value for requested protocol type
-//    const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
-
-//    // attempt to create socket
-//    int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
-
-//    // if we fetched an invalid socket descriptor
-//    if ( socketFd <= 0 ) {
-
-//        // see what error we got
-//        switch ( errno ) {
-//            case EPROTONOSUPPORT:
-//            case EAFNOSUPPORT:
-//            case EINVAL:
-//                m_socketError = TcpSocket::UnsupportedSocketOperationError;
-//                m_errorString = "protocol not supported";
-//                break;
-//            case ENFILE:
-//            case EMFILE:
-//            case ENOBUFS:
-//            case ENOMEM:
-//                m_socketError = TcpSocket::SocketResourceError;
-//                m_errorString = "out of resources";
-//                break;
-//            case EACCES:
-//                m_socketError = TcpSocket::SocketAccessError;
-//                m_errorString = "permission denied";
-//                break;
-//            default:
-//                break;
-//        }
-
-//        // return failure
-//        return false;
-//    }
-
-//    // otherwise, store our socket FD & return success
-//    m_socketDescriptor = socketFd;
-//    return true;
+    // get protocol value for requested protocol type
+    const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
+
+    // attempt to create socket
+    SOCKET socketFd = WSASocket(protocolNum, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
+
+    // if we fetched an invalid socket descriptor
+    if ( socketFd == INVALID_SOCKET ) {
+
+        // set error type/message depending on error code
+        const int errorCode = WSAGetLastError();
+        switch ( errorCode ) {
+            case WSANOTINITIALISED:
+                m_socketError = TcpSocket::UnknownSocketError;
+                m_errorString = "Windows socket functionality not properly initialized";
+                break;
+            case WSAEAFNOSUPPORT:
+            case WSAESOCKTNOSUPPORT:
+            case WSAEPROTOTYPE:
+            case WSAEINVAL:
+                m_socketError = TcpSocket::UnsupportedSocketOperationError;
+                m_errorString = "protocol not supported";
+                break;
+            case WSAEMFILE:
+            case WSAENOBUFS:
+                m_socketError = TcpSocket::SocketResourceError;
+                m_errorString = "out of resources";
+                break;
+            default:
+                break;
+        }
+
+        // return failure
+        return false;
+    }
+
+    // otherwise, store our socket FD & return success
+    m_socketDescriptor = static_cast<int>(socketFd);
+    return true;
 }
 
-//bool TcpSocketEngine::nativeFetchConnectionParameters(void) {
-
-//    // reset addresses/ports
-//    m_localAddress.Clear();
-//    m_remoteAddress.Clear();
-//    m_localPort  = 0;
-//    m_remotePort = 0;
-
-//    // skip (return failure) if invalid socket FD
-//    if ( m_socketDescriptor == -1 )
-//        return false;
-
-//    sockaddr sa;
-//    BT_SOCKLEN_T sockAddrSize = sizeof(sa);
-
-//    // fetch local address info
-//    memset(&sa, 0, sizeof(sa));
-//    if ( getsockname(m_socketDescriptor, &sa, &sockAddrSize) == 0 ) {
-//        getPortAndAddress(&sa, m_localPort, m_localAddress);
-//    }
-//    else if ( errno == EBADF ) {
-//        m_socketError = TcpSocket::UnsupportedSocketOperationError;
-//        m_errorString = "invalid socket descriptor";
-//        return false;
-//    }
-
-//    // fetch remote address
-//    if ( getpeername(m_socketDescriptor, &sa, &sockAddrSize) == 0 )
-//        getPortAndAddress(&sa, m_remotePort, m_remoteAddress);
-
-//    // return success
-//    return true;
-//}
-
-size_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
-
-//    // fetch number of bytes, return 0 on error
-//    int numBytes(0);
-//    if ( ioctl(m_socketDescriptor, FIONREAD, (char*)&numBytes) < 0 )
-//        return 0;
-//    return static_cast<size_t>(numBytes);
+int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
+
+    int64_t numBytes(0);
+    int64_t dummy(0);
+    DWORD bytesWritten(0);
+
+    const int ioctlResult = WSAIoctl( m_socketDescriptor, FIONREAD
+                                    , &dummy, sizeof(dummy)
+                                    , &numBytes, sizeof(numBytes)
+                                    , &bytesWritten, 0, 0
+                                    );
+    return ( ioctlResult == SOCKET_ERROR ? -1 : numBytes );
 }
 
 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
 
-//    if ( !IsValid() )
-//        return -1;
-
-//    ssize_t ret = read(m_socketDescriptor, dest, max);
-//    if ( ret < 0 ) {
-//        ret = -1;
-//        switch ( errno ) {
-//            case EAGAIN :
-//                // No data was available for reading
-//                ret = -2;
-//                break;
-//            case ECONNRESET :
-//                ret = 0;
-//                break;
-//            default:
-//                break;
-//        }
-//    }
-
-//    return static_cast<int64_t>(ret);
+    // skip if invalid socket
+    if ( !IsValid() )
+        return -1;
+
+    // set up our WSA output buffer
+    WSABUF buf;
+    buf.buf = dest;
+    buf.len = max;
+
+    // attempt to read bytes
+    DWORD flags = 0;
+    DWORD bytesRead = 0;
+    const int readResult = WSARecv(m_socketDescriptor, &buf, 1, &bytesRead, &flags, 0, 0);
+
+    // if error encountered
+    if ( readResult == SOCKET_ERROR ) {
+        const int errorCode = WSAGetLastError();
+        switch ( errorCode ) {
+            case WSAEWOULDBLOCK: // nothing read this time, but more coming later
+                return -2;
+            default:
+                return -1;        // on any other errors
+        }
+    }
+
+    //  check if nothing was read this time, but more is coming
+    if ( WSAGetLastError() == WSAEWOULDBLOCK )
+        return -2;
+
+    // otherwise return number of bytes read
+    return static_cast<int64_t>(bytesRead);
 }
 
 // negative value for msecs will block (forever) until
 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
 
-//    // set up FD set
-//    fd_set fds;
-//    FD_ZERO(&fds);
-//    FD_SET(m_socketDescriptor, &fds);
-
-//    // setup our timeout
-//    timeval tv;
-//    tv.tv_sec  = msecs / 1000;
-//    tv.tv_usec = (msecs % 1000) * 1000;
-
-//    // do 'select'
-//    int ret;
-//    if ( isRead )
-//        ret = select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
-//    else
-//        ret = select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
-//    return ret;
+    fd_set fds;
+    FD_ZERO(&fds);
+    FD_SET(m_socketDescriptor, &fds);
+
+    timeval tv;
+    tv.tv_sec  = msecs / 1000;
+    tv.tv_usec = (msecs % 1000) * 1000;
+
+    // do 'select'
+    if ( isRead )
+        return select(0, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
+    else
+        return select(0, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
 }
 
 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
 
-//    ssize_t writtenBytes = write(m_socketDescriptor, data, length);
-//    if ( writtenBytes < 0 ) {
-//        switch (errno) {
-//            case EPIPE:
-//            case ECONNRESET:
-//                writtenBytes = -1;
-//                m_socketError = TcpSocket::RemoteHostClosedError;
-//                m_errorString = "remote host closed connection";
-//                Close();
-//                break;
-//            case EAGAIN:
-//                writtenBytes = 0;
-//                break;
-//            default:
-//                break;
-//        }
-//    }
-
-//    return static_cast<int64_t>(writtenBytes);
+    // setup our WSA write buffer
+    WSABUF buf;
+    buf.buf = (char*)data;
+    buf.len = length;
+
+    // attempt to write bytes
+    DWORD flags = 0;
+    DWORD bytesWritten = 0;
+    const int writeResult = WSASend(m_socketDescriptor, &buf, 1, &bytesWritten, flags, 0, 0);
+
+    // error encountered
+    if ( writeResult == SOCKET_ERROR )  {
+
+        const int errorCode = WSAGetLastError();
+        switch ( errorCode ) {
+            case WSAEWOULDBLOCK:
+                return 0;
+            case WSAECONNRESET:
+            case WSAECONNABORTED:
+                m_socketError = TcpSocket::NetworkError;
+                m_errorString = "connection reset or aborted";
+                return -1;
+            default:
+                return -1;
+        }
+    }
+
+    // otherwise return number of bytes written
+    return static_cast<int64_t>(bytesWritten);
 }