1 // ***************************************************************************
2 // TcpSocketEngine_unix_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 UNIX-like systems
8 // ***************************************************************************
10 #include "api/internal/io/TcpSocketEngine_p.h"
11 #include "api/internal/io/NetUnix_p.h"
12 using namespace BamTools;
13 using namespace BamTools::Internal;
16 #include <sys/filio.h>
24 // ------------------------
25 // static utility methods
26 // ------------------------
31 } // namespace Internal
32 } // namespace BamTools
34 // --------------------------------
35 // TcpSocketEngine implementation
36 // --------------------------------
38 void TcpSocketEngine::nativeClose(void) {
39 close(m_socketDescriptor);
42 bool TcpSocketEngine::nativeConnect(const HostAddress& address, const uint16_t port) {
44 // setup connection parameters from address/port
45 sockaddr_in sockAddrIPv4;
46 sockaddr_in6 sockAddrIPv6;
47 sockaddr* sockAddrPtr = 0;
48 BT_SOCKLEN_T sockAddrSize = 0;
51 if ( address.GetProtocol() == HostAddress::IPv6Protocol ) {
53 memset(&sockAddrIPv6, 0, sizeof(sockAddrIPv6));
54 sockAddrIPv6.sin6_family = AF_INET6;
55 sockAddrIPv6.sin6_port = htons(port);
57 IPv6Address ip6 = address.GetIPv6Address();
58 memcpy(&sockAddrIPv6.sin6_addr.s6_addr, &ip6, sizeof(ip6));
60 sockAddrSize = sizeof(sockAddrIPv6);
61 sockAddrPtr = (sockaddr*)&sockAddrIPv6;
65 else if ( address.GetProtocol() == HostAddress::IPv4Protocol ) {
67 memset(&sockAddrIPv4, 0, sizeof(sockAddrIPv4));
68 sockAddrIPv4.sin_family = AF_INET;
69 sockAddrIPv4.sin_port = htons(port);
70 sockAddrIPv4.sin_addr.s_addr = htonl(address.GetIPv4Address());
72 sockAddrSize = sizeof(sockAddrIPv4);
73 sockAddrPtr = (sockaddr*)&sockAddrIPv4;
76 // unknown (should be unreachable)
77 else BT_ASSERT_X(false, "TcpSocketEngine::nativeConnect() : unknown network protocol");
80 int connectResult = connect(m_socketDescriptor, sockAddrPtr, sockAddrSize);
82 // if failed, handle error
83 if ( connectResult == -1 ) {
85 // ensure state is set before checking errno
86 m_socketState = TcpSocket::UnconnectedState;
88 // set error type/message depending on errno
89 switch ( errno ) { // <-- potential thread issues later? but can't get error type from connectResult
92 m_socketState = TcpSocket::ConnectedState; // socket was already connected
96 m_socketError = TcpSocket::ConnectionRefusedError;
97 m_errorString = "connection refused";
100 m_socketError = TcpSocket::NetworkError;
101 m_errorString = "connection timed out";
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";
117 m_socketError = TcpSocket::SocketAccessError;
118 m_errorString = "permission denied";
124 // double check that we're not in 'connected' state; if so, return failure
125 if ( m_socketState != TcpSocket::ConnectedState )
129 // otherwise, we should be good
130 // update state & return success
131 m_socketState = TcpSocket::ConnectedState;
135 bool TcpSocketEngine::nativeCreateSocket(HostAddress::NetworkProtocol protocol) {
137 // get protocol value for requested protocol type
138 const int protocolNum = ( (protocol == HostAddress::IPv6Protocol) ? AF_INET6
141 // attempt to create socket
142 int socketFd = socket(protocolNum, SOCK_STREAM, IPPROTO_TCP);
144 // if we fetched an invalid socket descriptor
145 if ( socketFd <= 0 ) {
147 // see what error we got
149 case EPROTONOSUPPORT:
152 m_socketError = TcpSocket::UnsupportedSocketOperationError;
153 m_errorString = "protocol not supported";
159 m_socketError = TcpSocket::SocketResourceError;
160 m_errorString = "out of resources";
163 m_socketError = TcpSocket::SocketAccessError;
164 m_errorString = "permission denied";
174 // otherwise, store our socket FD & return success
175 m_socketDescriptor = socketFd;
179 int64_t TcpSocketEngine::nativeNumBytesAvailable(void) const {
181 // fetch number of bytes, return 0 on error
183 if ( ioctl(m_socketDescriptor, FIONREAD, (char*)&numBytes) < 0 )
185 return static_cast<int64_t>(numBytes);
188 int64_t TcpSocketEngine::nativeRead(char* dest, size_t max) {
189 const ssize_t ret = read(m_socketDescriptor, dest, max);
190 return static_cast<int64_t>(ret);
193 // negative value for msecs will block (forever) until ready
194 int TcpSocketEngine::nativeSelect(int msecs, bool isRead) const {
199 FD_SET(m_socketDescriptor, &fds);
203 tv.tv_sec = msecs / 1000;
204 tv.tv_usec = (msecs % 1000) * 1000;
208 return select(m_socketDescriptor + 1, &fds, 0, 0, (msecs < 0 ? 0 : &tv));
210 return select(m_socketDescriptor + 1, 0, &fds, 0, (msecs < 0 ? 0 : &tv));
213 int64_t TcpSocketEngine::nativeWrite(const char* data, size_t length) {
214 const ssize_t writtenBytes = write(m_socketDescriptor, data, length);
215 return static_cast<int64_t>(writtenBytes);