From c9ded8b7ecb6bf17425df053b9c2f6858a77848e Mon Sep 17 00:00:00 2001 From: derek Date: Wed, 16 Nov 2011 21:02:26 -0500 Subject: [PATCH] Basic Windows implementation --- .../internal/io/TcpSocketEngine_unix_p.cpp | 90 +--- src/api/internal/io/TcpSocketEngine_win_p.cpp | 474 ++++++++---------- 2 files changed, 231 insertions(+), 333 deletions(-) diff --git a/src/api/internal/io/TcpSocketEngine_unix_p.cpp b/src/api/internal/io/TcpSocketEngine_unix_p.cpp index 35d8160..efcdf8d 100644 --- a/src/api/internal/io/TcpSocketEngine_unix_p.cpp +++ b/src/api/internal/io/TcpSocketEngine_unix_p.cpp @@ -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) { diff --git a/src/api/internal/io/TcpSocketEngine_win_p.cpp b/src/api/internal/io/TcpSocketEngine_win_p.cpp index cb3ddca..d1691ac 100644 --- a/src/api/internal/io/TcpSocketEngine_win_p.cpp +++ b/src/api/internal/io/TcpSocketEngine_win_p.cpp @@ -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 #include 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(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(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(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(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(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(bytesWritten); } -- 2.39.2