// 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
// ***************************************************************************
using namespace BamTools;
using namespace BamTools::Internal;
+#include <cstring>
#include <iostream>
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
// --------------------------------
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);
}