// TcpSocket_p.cpp (c) 2011 Derek Barnett
// Marth Lab, Department of Biology, Boston College
// ---------------------------------------------------------------------------
-// Last modified: 10 November 2011 (DB)
+// Last modified: 5 January 2012 (DB)
// ---------------------------------------------------------------------------
// Provides basic TCP I/O interface
// ***************************************************************************
using namespace BamTools::Internal;
#include <algorithm>
+#include <climits>
#include <sstream>
#include <vector>
using namespace std;
namespace Internal {
// constants
-static const size_t DEFAULT_BUFFER_SIZE = 0x4000;
+static const size_t DEFAULT_BUFFER_SIZE = 0x10000;
} // namespace Internal
} // namespace BamTools
, m_engine(0)
, m_cachedSocketDescriptor(-1)
, m_readBuffer(DEFAULT_BUFFER_SIZE)
- , m_error(TcpSocket::UnknownSocketError)
+ , m_error(TcpSocket::NoError)
, m_state(TcpSocket::UnconnectedState)
{ }
{
// skip if we're already connected
if ( m_state == TcpSocket::ConnectedState ) {
- m_error = TcpSocket::SocketResourceError;
+ m_error = TcpSocket::SocketResourceError;
m_errorString = "socket already connected";
return false;
}
// reset socket state
m_hostName = hostInfo.HostName();
- m_mode = mode;
+ m_mode = mode;
m_state = TcpSocket::UnconnectedState;
- m_error = TcpSocket::UnknownSocketError;
+ m_error = TcpSocket::NoError;
// m_localPort = 0;
m_remotePort = 0;
// m_localAddress.Clear();
// if host name was IP address ("x.x.x.x" or IPv6 format)
// otherwise host name was 'plain-text' ("www.foo.bar")
// we need to look up IP address(es)
- if ( hostAddress.HasIPAddress() )
+ if ( hostAddress.HasIPAddress() )
info.SetAddresses( vector<HostAddress>(1, hostAddress) );
else
info = HostInfo::Lookup(hostName, port);
}
// fetch data from socket, return 0 for success, -1 for failure
- // since this should be called in a loop, we'll pull the actual bytes on next iteration
- return ( ReadFromSocket() ? 0 : -1 );
+ // since this should be called in a loop,
+ // we'll pull the actual bytes from the buffer on next iteration
+ const int64_t socketBytesRead = ReadFromSocket();
+ if ( socketBytesRead < 0 ) {
+ // TODO: set error string/state ?
+ return -1;
+ }
+
+ // we should have data now in buffer, try to fetch requested amount
+ // if nothing in buffer, we will return 0 bytes read (signals EOF reached)
+ const size_t numBytesRead = m_readBuffer.Read(data, numBytes);
+ return static_cast<int64_t>(numBytesRead);
}
-bool TcpSocket::ReadFromSocket(void) {
+int64_t TcpSocket::ReadFromSocket(void) {
// check for any socket engine errors
if ( !m_engine->IsValid() ) {
m_errorString = "TcpSocket::ReadFromSocket - socket disconnected";
ResetSocketEngine();
- return false;
+ return -1;
}
// wait for ready read
bool timedOut;
- bool isReadyRead = m_engine->WaitForRead(5000, &timedOut);
+ const bool isReadyRead = m_engine->WaitForRead(5000, &timedOut);
// if not ready
if ( !isReadyRead ) {
// if we simply timed out
if ( timedOut ) {
+ // TODO: get add'l error info from engine ?
m_errorString = "TcpSocket::ReadFromSocket - timed out waiting for ready read";
- // get error from engine ?
- return false;
}
- // otherwise, there was an error
+ // otherwise, there was some other error
else {
+ // TODO: get add'l error info from engine ?
m_errorString = "TcpSocket::ReadFromSocket - encountered error while waiting for ready read";
- // get error from engine ?
- return false;
}
- }
- // #########################################################################
- // clean this up - smells funky, but it's a key step so it has to be right
- // #########################################################################
+ // return failure
+ return -1;
+ }
// get number of bytes available from socket
- // (if 0, still try to read some data so we don't trigger any OS event behavior
- // that respond to repeated access to a remote closed socket)
- int64_t bytesToRead = m_engine->NumBytesAvailable();
+ const int64_t bytesToRead = m_engine->NumBytesAvailable();
if ( bytesToRead < 0 ) {
+ // TODO: get add'l error info from engine ?
m_errorString = "TcpSocket::ReadFromSocket - encountered error while determining numBytesAvailable";
- // get error from engine ?
- return false;
+ return -1;
}
- else if ( bytesToRead == 0 )
- bytesToRead = 4096;
// make space in buffer & read from socket
char* buffer = m_readBuffer.Reserve(bytesToRead);
- int64_t numBytesRead = m_engine->Read(buffer, bytesToRead);
-
- // if error while reading
+ const int64_t numBytesRead = m_engine->Read(buffer, bytesToRead);
if ( numBytesRead == -1 ) {
+ // TODO: get add'l error info from engine ?
m_errorString = "TcpSocket::ReadFromSocket - encountered error while reading bytes";
- // get error from engine ?
- return false;
}
- // handle special case (no data, but not error)
- if ( numBytesRead == -2 )
- m_readBuffer.Chop(bytesToRead);
-
- // return success
- return true;
+ // return number of bytes actually read
+ return numBytesRead;
}
string TcpSocket::ReadLine(int64_t max) {
// prep result byte buffer
ByteArray result;
-
- size_t bufferMax = ((max > static_cast<int64_t>(string::npos)) ? string::npos : static_cast<size_t>(max));
+ size_t bufferMax = ((max > static_cast<int64_t>(UINT_MAX))
+ ? UINT_MAX : static_cast<size_t>(max));
result.Resize(bufferMax);
// read data
if ( result.Size() == 0 ) {
if ( bufferMax == 0 )
- bufferMax = string::npos;
+ bufferMax = UINT_MAX;
result.Resize(1);
int64_t readResult;
do {
- result.Resize( static_cast<size_t>(std::min(bufferMax, result.Size() + DEFAULT_BUFFER_SIZE)) );
+ result.Resize( static_cast<size_t>(min(bufferMax, result.Size() + DEFAULT_BUFFER_SIZE)) );
readResult = ReadLine(result.Data()+readBytes, result.Size()-readBytes);
if ( readResult > 0 || readBytes == 0 )
readBytes += readResult;
}
int64_t TcpSocket::ReadLine(char* dest, size_t max) {
-
+
// wait for buffer to contain line contents
if ( !WaitForReadLine() ) {
m_errorString = "TcpSocket::ReadLine - error waiting for read line";
return -1;
}
-
+
// leave room for null term
if ( max < 2 )
return -1;
// wait until we can read a line (will return immediately if already capable)
while ( !CanReadLine() ) {
- if ( !ReadFromSocket() )
+ if ( !ReadFromSocket() )
return false;
}
- // if we get here, success
+ // if we get here, success
return true;
}
// single-shot attempt at write (not buffered, just try to shove the data through socket)
// this method purely exists to send 'small' HTTP requests/FTP commands from client to server
- int64_t bytesWritten(0);
-
// wait for our socket to be write-able
bool timedOut;
- bool isReadyWrite = m_engine->WaitForWrite(3000, &timedOut);
+ const bool isReadyWrite = m_engine->WaitForWrite(3000, &timedOut);
+
+ // if ready, return number of bytes written
if ( isReadyWrite )
- bytesWritten = m_engine->Write(data, numBytes);
+ return m_engine->Write(data, numBytes);
+
+ // otherwise, socket not ready for writing
+ // set error string depending on reason & return failure
+ if ( !timedOut ) {
+ // TODO: get add'l error info from engine ??
+ m_errorString = "TcpSocket::Write - timed out waiting for ready-write";
+ }
else {
- // timeout is OK (with current setup), we'll just return 0 & try again
- // but we need to report if engine encountered some other error
- if ( !timedOut ) {
- // TODO: set error string
- bytesWritten = -1;
- }
+ // TODO: get add'l error info from engine ??
+ m_errorString = "TcpSocket::Write - error encountered while waiting for ready-write";
}
-
- // return actual number of bytes written to socket
- return bytesWritten;
+ return -1;
}