1 // ***************************************************************************
2 // TcpSocketEngine_win_p.cpp (c) 2011 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // ---------------------------------------------------------------------------
5 // Last modified: 8 December 2011 (DB)
6 // ---------------------------------------------------------------------------
7 // Provides low-level implementation of TCP I/O for all Windows systems
8 // ***************************************************************************
10 #include "api/internal/io/TcpSocketEngine_p.h"
11 #include "api/internal/io/NetWin_p.h"
12 using namespace BamTools;
13 using namespace BamTools::Internal;
20 // --------------------------------
21 // TcpSocketEngine implementation
22 // --------------------------------
24 void TcpSocketEngine::nativeClose(void) {
25 closesocket(m_socketDescriptor);
28 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
30 // setup connection parameters from address/port
31 sockaddr_in sockAddrIPv4;
32 sockaddr_in6 sockAddrIPv6;
33 sockaddr* sockAddrPtr = 0;
34 BT_SOCKLEN_T sockAddrSize = 0;
37 if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
39 memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
40 sockAddrIPv6.sin6_family = AF_INET6;
41 sockAddrIPv6.sin6_port = htons(port);
43 IPv6Address ip6 = address.GetIPv6Address();
44 memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
46 sockAddrSize = sizeof(sockAddrIPv6);
47 sockAddrPtr = (sockaddr*)&sockAddrIPv6;
51 else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
53 memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
54 sockAddrIPv4.sin_family = AF_INET;
55 sockAddrIPv4.sin_port = htons(port);
56 sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
58 sockAddrSize = sizeof(sockAddrIPv4);
59 sockAddrPtr = (sockaddr*)&sockAddrIPv4;
62 // unknown (should be unreachable)
63 else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
66 const int connectResult = WSAConnect(m_socketDescriptor, sockAddrPtr, sockAddrSize, 0, 0, 0, 0);
68 // if failed, handle error
69 if ( connectResult == SOCKET_ERROR ) {
71 // ensure state is set before checking error code
72 m_socketState = TcpSocket::UnconnectedState;
74 // set error type/message depending on errorCode
75 const int errorCode = WSAGetLastError();
76 switch ( errorCode ) {
77 case WSANOTINITIALISED:
78 m_socketError = TcpSocket::UnknownSocketError;
79 m_errorString = "Windows socket functionality not properly initialized";
82 m_socketState = TcpSocket::ConnectedState; // socket already connected
86 m_socketError = TcpSocket::ConnectionRefusedError;
87 m_errorString = "connection refused";
90 m_socketError = TcpSocket::NetworkError;
91 m_errorString = "connection timed out";
94 m_socketError = TcpSocket::NetworkError;
95 m_errorString = "host unreachable";
98 m_socketError = TcpSocket::NetworkError;
99 m_errorString = "network unreachable";
102 m_socketError = TcpSocket::SocketResourceError;
103 m_errorString = "address already in use";
106 m_socketError = TcpSocket::SocketAccessError;
107 m_errorString = "permission denied";
113 // double check that we're not in 'connected' state; if so, return failure
114 if ( m_socketState != TcpSocket::ConnectedState )
118 // otherwise, we should be good
119 // update state & return success
120 m_socketState = TcpSocket::ConnectedState;
124 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
126 // get protocol value for requested protocol type
127 const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
129 // attempt to create socket
130 SOCKET socketFd = WSASocket(protocolNum, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
132 // if we fetched an invalid socket descriptor
133 if ( socketFd == INVALID_SOCKET ) {
135 // set error type/message depending on error code
136 const int errorCode = WSAGetLastError();
137 switch ( errorCode ) {
138 case WSANOTINITIALISED:
139 m_socketError = TcpSocket::UnknownSocketError;
140 m_errorString = "Windows socket functionality not properly initialized";
142 case WSAEAFNOSUPPORT:
143 case WSAESOCKTNOSUPPORT:
146 m_socketError = TcpSocket::UnsupportedSocketOperationError;
147 m_errorString = "protocol not supported";
151 m_socketError = TcpSocket::SocketResourceError;
152 m_errorString = "out of resources";
155 m_socketError = TcpSocket::UnknownSocketError;
156 stringstream errStream("");
157 errStream << "WSA ErrorCode: " << errorCode;
158 m_errorString = errStream.str();
166 // otherwise, store our socket FD & return success
167 m_socketDescriptor = static_cast<int>(socketFd);
171 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
175 DWORD bytesWritten(0);
177 const int ioctlResult = WSAIoctl( m_socketDescriptor, FIONREAD
178 , &dummy, sizeof(dummy)
179 , &numBytes, sizeof(numBytes)
180 , &bytesWritten, 0, 0
182 return ( ioctlResult == SOCKET_ERROR ? -1 : numBytes );
185 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
187 // skip if invalid socket
191 // set up our WSA output buffer
196 // attempt to read bytes
199 const int readResult = WSARecv(m_socketDescriptor, &buf, 1, &bytesRead, &flags, 0, 0);
200 if ( readResult == SOCKET_ERROR )
203 // return number of bytes read
204 return static_cast<int64_t>(bytesRead);
207 // negative value for msecs will block (forever) until
208 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
212 FD_SET(m_socketDescriptor, &fds);
215 tv.tv_sec = msecs / 1000;
216 tv.tv_usec = (msecs % 1000) * 1000;
220 return select(0, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
222 return select(0, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
225 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
227 // setup our WSA write buffer
229 buf.buf = (char*)data;
232 // attempt to write bytes
234 DWORD bytesWritten = 0;
235 const int writeResult = WSASend(m_socketDescriptor, &buf, 1, &bytesWritten, flags, 0, 0);
236 if ( writeResult == SOCKET_ERROR )
239 // return number of bytes written
240 return static_cast<int64_t>(bytesWritten);