1 // ***************************************************************************
2 // TcpSocketEngine_win_p.cpp (c) 2011 Derek Barnett
3 // Marth Lab, Department of Biology, Boston College
4 // ---------------------------------------------------------------------------
5 // Last modified: 15 November 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;
19 // ------------------------
20 // static utility methods
21 // ------------------------
27 } // namespace Internal
28 } // namespace BamTools
30 // --------------------------------
31 // TcpSocketEngine implementation
32 // --------------------------------
34 void TcpSocketEngine::nativeClose(void) {
35 closesocket(m_socketDescriptor);
38 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
40 // setup connection parameters from address/port
41 sockaddr_in sockAddrIPv4;
42 sockaddr_in6 sockAddrIPv6;
43 sockaddr* sockAddrPtr = 0;
44 BT_SOCKLEN_T sockAddrSize = 0;
47 if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
49 memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
50 sockAddrIPv6.sin6_family = AF_INET6;
51 sockAddrIPv6.sin6_port = htons(port);
53 IPv6Address ip6 = address.GetIPv6Address();
54 memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
56 sockAddrSize = sizeof(sockAddrIPv6);
57 sockAddrPtr = (sockaddr*)&sockAddrIPv6;
61 else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
63 memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
64 sockAddrIPv4.sin_family = AF_INET;
65 sockAddrIPv4.sin_port = htons(port);
66 sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
68 sockAddrSize = sizeof(sockAddrIPv4);
69 sockAddrPtr = (sockaddr*)&sockAddrIPv4;
72 // unknown (should be unreachable)
73 else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
76 const int connectResult = WSAConnect(m_socketDescriptor, sockAddrPtr, sockAddrSize, 0, 0, 0, 0);
78 // if failed, handle error
79 if ( connectResult == SOCKET_ERROR ) {
81 // ensure state is set before checking error code
82 m_socketState = TcpSocket::UnconnectedState;
84 // set error type/message depending on errorCode
85 const int errorCode = WSAGetLastError();
86 switch ( errorCode ) {
87 case WSANOTINITIALISED:
88 m_socketError = TcpSocket::UnknownSocketError;
89 m_errorString = "Windows socket functionality not properly initialized";
92 m_socketState = TcpSocket::ConnectedState; // socket already connected
96 m_socketError = TcpSocket::ConnectionRefusedError;
97 m_errorString = "connection refused";
100 m_socketError = TcpSocket::NetworkError;
101 m_errorString = "connection timed out";
103 case WSAEHOSTUNREACH:
104 m_socketError = TcpSocket::NetworkError;
105 m_errorString = "host unreachable";
108 m_socketError = TcpSocket::NetworkError;
109 m_errorString = "network unreachable";
112 m_socketError = TcpSocket::SocketResourceError;
113 m_errorString = "address already in use";
116 m_socketError = TcpSocket::SocketAccessError;
117 m_errorString = "permission denied";
123 // double check that we're not in 'connected' state; if so, return failure
124 if ( m_socketState != TcpSocket::ConnectedState )
128 // otherwise, we should be good
129 // update state & return success
130 m_socketState = TcpSocket::ConnectedState;
134 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
136 // get protocol value for requested protocol type
137 const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6 : AF_INET );
139 // attempt to create socket
140 SOCKET socketFd = WSASocket(protocolNum, SOCK_STREAM, IPPROTO_TCP, 0, 0, WSA_FLAG_OVERLAPPED);
142 // if we fetched an invalid socket descriptor
143 if ( socketFd == INVALID_SOCKET ) {
145 // set error type/message depending on error code
146 const int errorCode = WSAGetLastError();
147 switch ( errorCode ) {
148 case WSANOTINITIALISED:
149 m_socketError = TcpSocket::UnknownSocketError;
150 m_errorString = "Windows socket functionality not properly initialized";
152 case WSAEAFNOSUPPORT:
153 case WSAESOCKTNOSUPPORT:
156 m_socketError = TcpSocket::UnsupportedSocketOperationError;
157 m_errorString = "protocol not supported";
161 m_socketError = TcpSocket::SocketResourceError;
162 m_errorString = "out of resources";
172 // otherwise, store our socket FD & return success
173 m_socketDescriptor = static_cast<int>(socketFd);
177 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
181 DWORD bytesWritten(0);
183 const int ioctlResult = WSAIoctl( m_socketDescriptor, FIONREAD
184 , &dummy, sizeof(dummy)
185 , &numBytes, sizeof(numBytes)
186 , &bytesWritten, 0, 0
188 return ( ioctlResult == SOCKET_ERROR ? -1 : numBytes );
191 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
193 // skip if invalid socket
197 // set up our WSA output buffer
202 // attempt to read bytes
205 const int readResult = WSARecv(m_socketDescriptor, &buf, 1, &bytesRead, &flags, 0, 0);
207 // if error encountered
208 if ( readResult == SOCKET_ERROR ) {
209 const int errorCode = WSAGetLastError();
210 switch ( errorCode ) {
211 case WSAEWOULDBLOCK: // nothing read this time, but more coming later
214 return -1; // on any other errors
218 // check if nothing was read this time, but more is coming
219 if ( WSAGetLastError() == WSAEWOULDBLOCK )
222 // otherwise return number of bytes read
223 return static_cast<int64_t>(bytesRead);
226 // negative value for msecs will block (forever) until
227 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
231 FD_SET(m_socketDescriptor, &fds);
234 tv.tv_sec = msecs / 1000;
235 tv.tv_usec = (msecs % 1000) * 1000;
239 return select(0, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
241 return select(0, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
244 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
246 // setup our WSA write buffer
248 buf.buf = (char*)data;
251 // attempt to write bytes
253 DWORD bytesWritten = 0;
254 const int writeResult = WSASend(m_socketDescriptor, &buf, 1, &bytesWritten, flags, 0, 0);
257 if ( writeResult == SOCKET_ERROR ) {
259 const int errorCode = WSAGetLastError();
260 switch ( errorCode ) {
264 case WSAECONNABORTED:
265 m_socketError = TcpSocket::NetworkError;
266 m_errorString = "connection reset or aborted";
273 // otherwise return number of bytes written
274 return static_cast<int64_t>(bytesWritten);