]> git.donarmstrong.com Git - bamtools.git/blob - src/api/internal/io/TcpSocketEngine_win_p.cpp
Fixed: premature EOF issues & updated Windows implementation
[bamtools.git] / src / api / internal / io / TcpSocketEngine_win_p.cpp
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 // ***************************************************************************
9
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;
14
15 #include <cstring>
16 #include <iostream>
17 #include <sstream>
18 using namespace std;
19
20 // --------------------------------
21 // TcpSocketEngine implementation
22 // --------------------------------
23
24 void TcpSocketEngine::nativeClose(void) {
25     closesocket(m_socketDescriptor);
26 }
27
28 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
29
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;
35
36     // IPv6
37     if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
38
39         memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
40         sockAddrIPv6.sin6_family = AF_INET6;
41         sockAddrIPv6.sin6_port   = htons(port);
42
43         IPv6Address ip6 = address.GetIPv6Address();
44         memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
45
46         sockAddrSize = sizeof(sockAddrIPv6);
47         sockAddrPtr  = (sockaddr*)&sockAddrIPv6;
48     }
49
50     // IPv4
51     else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
52
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());
57
58         sockAddrSize = sizeof(sockAddrIPv4);
59         sockAddrPtr  = (sockaddr*)&sockAddrIPv4;
60     }
61
62     // unknown (should be unreachable)
63     else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
64
65     // attempt conenction
66     const int connectResult = WSAConnect(m_socketDescriptor, sockAddrPtr, sockAddrSize, 0, 0, 0, 0);
67
68     // if failed, handle error
69     if ( connectResult == SOCKET_ERROR ) {
70
71         // ensure state is set before checking error code
72         m_socketState = TcpSocket::UnconnectedState;
73
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";
80                 break;
81             case WSAEISCONN:
82                 m_socketState = TcpSocket::ConnectedState; // socket already connected
83                 break;
84             case WSAECONNREFUSED:
85             case WSAEINVAL:
86                 m_socketError = TcpSocket::ConnectionRefusedError;
87                 m_errorString = "connection refused";
88                 break;
89             case WSAETIMEDOUT:
90                 m_socketError = TcpSocket::NetworkError;
91                 m_errorString = "connection timed out";
92                 break;
93             case WSAEHOSTUNREACH:
94                 m_socketError = TcpSocket::NetworkError;
95                 m_errorString = "host unreachable";
96                 break;
97             case WSAENETUNREACH:
98                 m_socketError = TcpSocket::NetworkError;
99                 m_errorString = "network unreachable";
100                 break;
101             case WSAEADDRINUSE:
102                 m_socketError = TcpSocket::SocketResourceError;
103                 m_errorString = "address already in use";
104                 break;
105             case WSAEACCES:
106                 m_socketError = TcpSocket::SocketAccessError;
107                 m_errorString = "permission denied";
108                 break;
109             default:
110                 break;
111         }
112
113         // double check that we're not in 'connected' state; if so, return failure
114         if ( m_socketState != TcpSocket::ConnectedState )
115             return false;
116     }
117
118     // otherwise, we should be good
119     // update state & return success
120     m_socketState = TcpSocket::ConnectedState;
121     return true;
122 }
123
124 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
125
126     // get protocol value for requested protocol type
127     const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
128
129     // attempt to create socket
130     SOCKET socketFd = WSASocket(protocolNum, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
131
132     // if we fetched an invalid socket descriptor
133     if ( socketFd == INVALID_SOCKET ) {
134
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";
141                 break;
142             case WSAEAFNOSUPPORT:
143             case WSAESOCKTNOSUPPORT:
144             case WSAEPROTOTYPE:
145             case WSAEINVAL:
146                 m_socketError = TcpSocket::UnsupportedSocketOperationError;
147                 m_errorString = "protocol not supported";
148                 break;
149             case WSAEMFILE:
150             case WSAENOBUFS:
151                 m_socketError = TcpSocket::SocketResourceError;
152                 m_errorString = "out of resources";
153                 break;
154             default:
155                 m_socketError = TcpSocket::UnknownSocketError;
156                 stringstream errStream("");
157                 errStream << "WSA ErrorCode: " << errorCode;
158                 m_errorString = errStream.str();
159                 break;
160         }
161
162         // return failure
163         return false;
164     }
165
166     // otherwise, store our socket FD & return success
167     m_socketDescriptor = static_cast<int>(socketFd);
168     return true;
169 }
170
171 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
172
173     int64_t numBytes(0);
174     int64_t dummy(0);
175     DWORD bytesWritten(0);
176
177     const int ioctlResult = WSAIoctl( m_socketDescriptor, FIONREAD
178                                     , &dummy, sizeof(dummy)
179                                     , &numBytes, sizeof(numBytes)
180                                     , &bytesWritten, 0, 0
181                                     );
182     return ( ioctlResult == SOCKET_ERROR ? -1 : numBytes );
183 }
184
185 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
186
187     // skip if invalid socket
188     if ( !IsValid() )
189         return -1;
190
191     // set up our WSA output buffer
192     WSABUF buf;
193     buf.buf = dest;
194     buf.len = max;
195
196     // attempt to read bytes
197     DWORD flags = 0;
198     DWORD bytesRead = 0;
199     const int readResult = WSARecv(m_socketDescriptor, &buf, 1, &bytesRead, &flags, 0, 0);
200     if ( readResult == SOCKET_ERROR )
201         return -1;
202
203     // return number of bytes read
204     return static_cast<int64_t>(bytesRead);
205 }
206
207 // negative value for msecs will block (forever) until
208 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
209
210     fd_set fds;
211     FD_ZERO(&fds);
212     FD_SET(m_socketDescriptor, &fds);
213
214     timeval tv;
215     tv.tv_sec  = msecs / 1000;
216     tv.tv_usec = (msecs % 1000) * 1000;
217
218     // do 'select'
219     if ( isRead )
220         return select(0, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
221     else
222         return select(0, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
223 }
224
225 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
226
227     // setup our WSA write buffer
228     WSABUF buf;
229     buf.buf = (char*)data;
230     buf.len = length;
231
232     // attempt to write bytes
233     DWORD flags = 0;
234     DWORD bytesWritten = 0;
235     const int writeResult = WSASend(m_socketDescriptor, &buf, 1, &bytesWritten, flags, 0, 0);
236     if ( writeResult == SOCKET_ERROR )
237         return -1;
238
239     // return number of bytes written
240     return static_cast<int64_t>(bytesWritten);
241 }