paradigm_3/html/
/*! \file socket.cpp
  This is the Socket class implementation.

  \author Jon A. Lambert
  \date 05/02/2003
  \version 0.30

  \note
  Unix/BSD portability notes:
   - Either read() or recv() may be used.
   - Winsock errors are prepended with WSA modify accordingly.
   - WSAGetLastError() should be replaced with global errno variable.
 */
#include "sysconfig.h"
#include "socket.h"

/*!
 The constructor for Socket

 \param sock The network socket to be associated with this Socket.
 \param size The size to allocate the both input and output buffers.
             Defaults to 4096.
 */
Socket::Socket(SOCKET sock, int size) : mSock(sock), mSize(size) {
  mpInBuffer = new char[mSize];
  mpEod = mpRead = mpInBuffer;
  mpOutBuffer = new char[mSize];
  mpWrite = mpStartWrite = mpOutBuffer;
}

/*!
 The copy constructor for Socket

 \param r_sock The Socket to copy.
 */
Socket::Socket(const Socket& r_sock)
  : mSock(r_sock.mSock), mSize(r_sock.mSize) {
  // Set up new input buffer and pointers exactly
  mpInBuffer = new char[mSize];
  memcpy(mpInBuffer, r_sock.mpInBuffer, mSize);
  mpEod = mpInBuffer + (r_sock.mpEod - r_sock.mpInBuffer);
  mpRead = mpInBuffer + (r_sock.mpRead - r_sock.mpInBuffer);
  // Set up new output buffer and pointers exactly
  mpOutBuffer = new char[mSize];
  memcpy(mpOutBuffer, r_sock.mpOutBuffer, mSize);
  mpWrite = mpOutBuffer + (r_sock.mpWrite - r_sock.mpOutBuffer);
  mpStartWrite = mpOutBuffer + (r_sock.mpStartWrite - r_sock.mpOutBuffer);
}

/*!
 Destructor for Socket
 */
Socket::~Socket() {
  delete[] mpInBuffer;
  delete[] mpOutBuffer;
}

/*!
 Read fetches a single character from the socket stream.  We buffer
 our reads here.

 \return char A single character read from the socket stream.

 \throw ShutdownException Indicates that our connection has closed.
 \throw BlockingException Normal condition that indicates the end of data
 \throw IOErrorException This is a non-recoverable error that could indicate
        any number of conditions.
 */
char Socket::Read() {
  if (mpRead == mpEod) {              // Nothing in bufffer. Fetch more data.
    mpEod = mpRead = mpInBuffer;      // reset buffer for a full load.
    int bytes = recv (mSock, mpInBuffer, mSize, 0);
    if (bytes == 0) {
      throw ShutdownException();      // Connection was shutdown.
    } else if (bytes == SOCKET_ERROR) {
      int err = WSAGetLastError();
      if (err == WSAEWOULDBLOCK) {
        throw BlockingException(err); // No more input available.
      } else {
        throw IOErrorException(err);  // We have a serious error.
      }
    }
    mpEod += bytes;
  }
  char c = *mpRead;
  mpRead++;
  return c;
}

/*!
 Write pushes a single character to the socket stream.  We buffer
 our writes here.  One must call flush to actually send the data in
 the buffer, although the buffer will automatically flush itself
 when it reaches mSize.

 \param c A single character to be pushed to the socket stream.

 \throw ShutdownException Indicates that our connection has closed.
 \throw BlockingException Normal condition that indicates the end of data
 \throw IOErrorException This is a non-recoverable error that could indicate
        any number of conditions.

 \note
 Any exceptions will be thrown from flush().
 */
void Socket::Write(const char c) {
  if (mpWrite == mpOutBuffer + mSize) {
    // There is no room in buffer - attempt to flush it.
    Flush();
  }
  *mpWrite = c;
  mpWrite++;
  return;
}

/*!
 Write pushes the contents of a string to the socket stream.

 \param r_str A text string to be pushed to the socket stream.

 \throw ShutdownException Indicates that our connection has closed.
 \throw BlockingException Normal condition that indicates the end of data
 \throw IOErrorException This is a non-recoverable error that could indicate
        any number of conditions.

 \note
 Any exceptions will be thrown from flush() in called character Write.

 \todo
 We need a nice way to handle partially written strings here in the odd
 case that send blocks in the middle of this Write.
 */
void Socket::Write(const string& r_str) {
  string::const_iterator si = r_str.begin();
  while(si != r_str.end()) {
    Write(*si);
    si++;
  }
}

/*!
 Write pushes the contents of a null terminated character array to the
 socket stream.

 \param p_char A null terminated character array to be pushed to the
        socket stream.

 \throw ShutdownException Indicates that our connection has closed.
 \throw BlockingException Normal condition that indicates the end of data
 \throw IOErrorException This is a non-recoverable error that could indicate
        any number of conditions.

 \note
 Any exceptions will be thrown from flush() in called character Write.

 \todo
 We need a nice way to handle partially written character arrays here in
 the odd case that send blocks in the middle of this Write.
 */
void Socket::Write(const char* p_char) {
  while(p_char) {
    Write(*p_char);
    p_char++;
  }
}

/*!
 Write pushes the contents of a character array to the socket stream.

 \param p_char An array of characters to be pushed to the socket stream.
 \param len The number of characters to be pushed to the socket stream.

 \throw ShutdownException Indicates that our connection has closed.
 \throw BlockingException Normal condition that indicates the end of data
 \throw IOErrorException This is a non-recoverable error that could indicate
        any number of conditions.

 \note
 Any exceptions will be thrown from flush() in called character Write.

 \todo
 We need a nice way to handle partially written character arrays here in
 the odd case that send blocks in the middle of this Write.
 */
void Socket::Write(const char* p_char, int len) {
  while(p_char && len) {
    Write(*p_char);
    p_char++;
    len--;
  }
}

/*!
 Flush sends the contents of the mOutBuffer to the socket stream.
 One must call flush to actually send the data in the buffer, although
 the buffer will automatically flush itself when it reaches mSize.

 \throw ShutdownException Indicates that our connection has closed.
 \throw BlockingException Normal condition that indicates the end of data
 \throw IOErrorException This is a non-recoverable error that could indicate
        any number of conditions.
 */
void Socket::Flush() {
  int wlen = mpWrite - mpStartWrite;
  if (wlen == 0) {  // Nothing to write
    if (mpStartWrite == mpOutBuffer + mSize) { // Oh we're at end of buffer
      mpWrite = mpStartWrite = mpOutBuffer; // reinitialize buffer
    }
    return;
  }
  int bytes = send (mSock, mpStartWrite, wlen, 0);
  if (bytes == SOCKET_ERROR) {
    int err = WSAGetLastError ();
    if (err == WSAEWOULDBLOCK) {
      throw BlockingException(); // will block
    } else {
      throw IOErrorException(err); // severe error
    }
  } else if (bytes < wlen) {  // We haven't written it all
    mpStartWrite += bytes;
    return;
  }
  // Okay everything was written - reinitalize buffer
  mpWrite = mpStartWrite = mpOutBuffer; // reinitialize buffer
  return;
}

/*!
 GetSocket gets the sockets value.

 \return the Socket's SOCKET value.
 */
SOCKET Socket::GetSocket() {
  return mSock;
}

/*!
 Close is called to close a Socket stream.
 */
void Socket::Close() {
  shutdown(mSock, SD_SEND); // Inform the client so they don't hang
  try {
    // Attempt to read all the data left in the socket's buffer.  This
    // should throw an exception which we will catch gracefully.
    while (true) Read();
  }
  catch (exception &ex) {
    closesocket(mSock);
  }
}