// cwinsock.cpp : implementation file
//              

#include "stdafx.h"
#include <memory.h>
#include <stdlib.h>
#include "cwinsock.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CWinSock
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CWinSock constructor
//
// Constructs the CWinSock object. Initializes member variables
//
CWinSock::CWinSock(WORD wVersionRequired/*= MAKEWORD(1, 1)*/)
{
  // initialize member variables
  m_wVersionRequired = wVersionRequired;
  m_nLastError = 0;
}

/////////////////////////////////////////////////////////////////////////////
// CWinSock::Startup()
//
// Start the WinSock sub-system.
//
int CWinSock::Startup()
{
  int nStatus = CWINSOCK_NOERROR;

  m_nLastError = WSAStartup(m_wVersionRequired, &m_wsaData);

  if (m_nLastError != 0)
    nStatus = CWINSOCK_WINSOCK_ERROR;

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CWinSock::Shutdown()
//
// Shutdown the WinSock sub-system.
//
int CWinSock::Shutdown()
{
  int nStatus = CWINSOCK_NOERROR;

  if (WSACleanup() != 0)
  {
    m_nLastError = WSAGetLastError();
    nStatus = CWINSOCK_WINSOCK_ERROR;
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CWinSock::Information()
//
// Copy the WinSock information structure.
//
void CWinSock::Information(LPWSADATA pwsaData)
{
  memcpy(pwsaData, &m_wsaData, sizeof(WSADATA));
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket constructor
//
// Constructs the CDatagramSocket object. Initializes member variables
//
CDatagramSocket::CDatagramSocket(CWnd *pParentWnd, UINT uMsg)
{
  // initialize member variables
  m_pParentWnd = pParentWnd;
  ASSERT(m_pParentWnd != NULL);
  m_uMsg = uMsg;
  ASSERT(m_uMsg != 0);
  InitVars();
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket destructor
//
CDatagramSocket::~CDatagramSocket()
{
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::InitVars()
//
// Initialize class member variables.
//
void CDatagramSocket::InitVars(BOOL bInitLastError/*= TRUE*/)
{
  if (bInitLastError)
    m_nLastError = 0;

  m_s = INVALID_SOCKET;
  memset(&m_sinLocal, 0, sizeof(SOCKADDR_IN));
  m_bServer = FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock.  Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// port number, in host order, as input.  A port number
// should only be specified if the socket is to be bound
// to a certain port.  If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CDatagramSocket::CreateSocket(int nLocalPort)
{
  // if this version of the function is being called,
  // a valid port number must be specified
  if (nLocalPort <= 0)
    return CWINSOCK_PROGRAMMING_ERROR;

  // convert the port number into a string and
  // call the version of CreateSocket() which
  // accepts a string
  char pszLocalService[18];
  _itoa(nLocalPort, pszLocalService, 10);
  return CreateSocket(pszLocalService);
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock.  Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// string containing a service name or port number.
// A parameter should only be specified if the socket is to be
// bound to a certain port.  If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CDatagramSocket::CreateSocket(LPSTR pszLocalService/*= NULL*/)
{
  int nStatus = CWINSOCK_NOERROR;

  while (1)
  {
    // Make sure the socket isn't already created.
    // If the socket handle is valid, return from this
    // function right away so the existing parameters of
    // the object are not tampered with.
    if (m_s != INVALID_SOCKET)
      return CWINSOCK_PROGRAMMING_ERROR;

    InitVars();

    // create the hidden window
    RECT rect;
    rect.left = 0;
    rect.top = 0;
    rect.right = 100;
    rect.bottom = 100;
    if (Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect, m_pParentWnd, 0) == 0)
    {
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }

    // create the socket
    m_s = socket(PF_INET, SOCK_DGRAM, 0);
    if (m_s == INVALID_SOCKET)
    {
      m_nLastError = WSAGetLastError();
      nStatus = CWINSOCK_WINSOCK_ERROR;
      DestroyWindow();
      break;
    }

    // If pszLocalService is not NULL, this is a server socket
    // that will accept data on the specified port.
    if (pszLocalService != NULL)
    {
      // this socket is bound to a port number
      // so set the server flag
      m_bServer = TRUE;

      // assign the address family
      m_sinLocal.sin_family = AF_INET;

      // assign the service port (may have to do a database lookup
      // if a service port number was not specified)
      m_sinLocal.sin_port = htons(atoi(pszLocalService));
      if (m_sinLocal.sin_port == 0)
      {
        LPSERVENT pSent = getservbyname(pszLocalService, "udp");
        if (pSent == NULL)
        {
          m_nLastError = WSAGetLastError();
          nStatus = CWINSOCK_WINSOCK_ERROR;
          closesocket(m_s);
          DestroyWindow();
          break;
        }
        m_sinLocal.sin_port = pSent->s_port;
      }

      // assign the IP address
      m_sinLocal.sin_addr.s_addr = htonl(INADDR_ANY);

      // bind the server socket to the name containing the port
      if (bind(m_s, (LPSOCKADDR)&m_sinLocal, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
      {
        m_nLastError = WSAGetLastError();
        nStatus = CWINSOCK_WINSOCK_ERROR;
        closesocket(m_s);
        DestroyWindow();
        break;
      }
    }

    // start asynchronous event notification
    long lEvent = FD_READ | FD_WRITE;
    if (WSAAsyncSelect(m_s, m_hWnd, CWINSOCK_EVENT_NOTIFICATION, lEvent) ==
     SOCKET_ERROR)
    {
      m_nLastError = WSAGetLastError();
      nStatus = CWINSOCK_WINSOCK_ERROR;
      closesocket(m_s);
      DestroySocket();
      break;
    }

    break;
  }

  // if anything failed in this function, set the
  // socket variables appropriately
  if (nStatus != CWINSOCK_NOERROR)
    InitVars(FALSE);

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::DestroySocket()
//
// Close the socket, remove any queued data,
// and destroy the hidden window.
//
int CDatagramSocket::DestroySocket()
{
  int nStatus = CWINSOCK_NOERROR;

  // make sure the socket is valid
  if (m_s == INVALID_SOCKET)
    nStatus = CWINSOCK_PROGRAMMING_ERROR;
  else
  {
    // remove any data in the write queue
    while (!m_listWrite.IsEmpty())
    {
      LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listWrite.RemoveHead();
      LPVOID pData = pDatagramData->pData;
      delete pDatagramData;

      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
       (LPARAM)pData);
    }

    // remove any data in the read queue
    while (!m_listRead.IsEmpty())
    {
      LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listRead.RemoveHead();
      free(pDatagramData->pData);
      delete pDatagramData;
    }

    // close the socket and initialize variables
    closesocket(m_s);
    InitVars();

    // destroy the hidden window
    DestroyWindow();
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Write()
//
// Write data to the socket specified by the name and port.
//
// This version of the Write() function takes an integer
// representing the length of the data to send, a pointer
// to the data to send, a pointer to a string representing
// the host name to send the data to, and an integer
// representing the port number to send to.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CDatagramSocket::Write(int nLen, LPVOID pData,
 LPSTR pszRemoteName, int nRemotePort)
{
  // convert the port number into a string and
  // call the version of Write() which accepts
  // a string service name or number
  char pszRemoteService[18];
  _itoa(nRemotePort, pszRemoteService, 10);
  return Write(nLen, pData, pszRemoteName, pszRemoteService);
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Write()
//
// Write data to the socket specified by the name and service
// name or number.
//
// This version of the Write() function takes an integer
// representing the length of the data to send, a pointer
// to the data to send, a pointer to a string representing
// the host name to send the data to, and a string representing
// the service name or port number to send the data to.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CDatagramSocket::Write(int nLen, LPVOID pData,
 LPSTR pszRemoteName, LPSTR pszRemoteService)
{
  int nStatus = CWINSOCK_NOERROR; // error status
  LPHOSTENT pHent;                // pointer to host entry structure
  LPSERVENT pSent;                // pointer to service entry structure
  SOCKADDR_IN sinRemote;          // Internet address of destination

  while (1)
  {
    // assign the address family
    sinRemote.sin_family = AF_INET;

    // assign the service port (may have to do a database lookup
    // if a service port number was not specified)
    sinRemote.sin_port = htons(atoi(pszRemoteService));
    if (sinRemote.sin_port == 0)
    {
      pSent = getservbyname(pszRemoteService, "udp");
      if (pSent == NULL)
      {
        m_nLastError = WSAGetLastError();
        nStatus = CWINSOCK_WINSOCK_ERROR;
        break;
      }
      sinRemote.sin_port = pSent->s_port;
    }

    // assign the IP address (may have to do a database lookup
    // if a dotted decimal IP address was not specified)
    sinRemote.sin_addr.s_addr = inet_addr(pszRemoteName);
    if (sinRemote.sin_addr.s_addr == INADDR_NONE)
    {
      pHent = gethostbyname(pszRemoteName);
      if (pHent == NULL)
      {
        m_nLastError = WSAGetLastError();
        nStatus = CWINSOCK_WINSOCK_ERROR;
        break;
      }
      sinRemote.sin_addr.s_addr = *(u_long *)pHent->h_addr;
    }

    // call the version of Write() that takes an
    // Internet address structure
    return Write(nLen, pData, &sinRemote);
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Write()
//
// Write data to the socket specified by the Internet address.
//
// This version of the Write() function takes an integer
// representing the length of the data to send, a pointer
// to the data to send, and a pointer to an Internet address
// structure to send the data to.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CDatagramSocket::Write(int nLen, LPVOID pData, LPSOCKADDR_IN psinRemote)
{
  int nStatus = CWINSOCK_NOERROR;

  while (1)
  {
    // dynamically allocate a structure to hold the
    // data pointer, the data's length, and the destination address
    LPDATAGRAMDATA pDatagramData = new DATAGRAMDATA;
    if (pDatagramData == NULL)
    {
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }
    pDatagramData->pData = pData;
    pDatagramData->nLen = nLen;
    memcpy(&(pDatagramData->sin), psinRemote, sizeof(SOCKADDR_IN));
  
    // add the data to the list
    TRY
    {
      m_listWrite.AddTail(pDatagramData);
    }
    CATCH (CMemoryException, e)
    {
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }
    END_CATCH

    // trigger the FD_WRITE handler to try to send
    PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
    break;
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::Read()
//
// Read data that has been received by the socket.
//
// This function takes a pointer to an integer that will be filled
// with the length of the data read and an optional pointer
// to an Internet address structure that will be filled with
// the address of the sender of the data.
// 
// A pointer to the data is returned on success.  The application
// using this object must free this pointer.  NULL is returned on failure.
//
LPVOID CDatagramSocket::Read(LPINT pnLen, LPSOCKADDR_IN psinRemote/*= NULL*/)
{
  LPVOID pData = NULL;

  // check to see if there is data to retrieve
  if (!m_listRead.IsEmpty())
  {
    // remove the stream data from the list
    LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listRead.RemoveHead();
    pData = pDatagramData->pData;
    *pnLen = pDatagramData->nLen;
    if (psinRemote != NULL)
      memcpy(psinRemote, &(pDatagramData->sin), sizeof(SOCKADDR_IN));
    delete pDatagramData;
  }

  return pData;
}

// message map
BEGIN_MESSAGE_MAP(CDatagramSocket, CWnd)
  //{{AFX_MSG_MAP(CDatagramSocket)
  //}}AFX_MSG_MAP
  ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::OnWinSockEvent()
//
// Called when there is an asynchronous event on the socket.
//
LONG CDatagramSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam)
{
  // check for an error
  if (WSAGETSELECTERROR(lParam) != 0)
    return 0L;

  // what event are we being notified of?
  switch (WSAGETSELECTEVENT(lParam))
  {
    case FD_READ:
      return HandleRead(wParam, lParam);
      break;
    case FD_WRITE:
      return HandleWrite(wParam, lParam);
      break;
    default:
      // this should never happen
      ASSERT(0);
      break;
  }

  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::HandleRead()
//
// Called when there is an asynchronous read event on the socket.
//
// If the read was successful, the data, its length, and the address
// of the sender of the data, are stored in the read queue.  Upon
// a successful read, the application window using this object is
// then notified with the m_uMsg message (wParam set to
// CWINSOCK_DONE_READING; lParam set to the number of data chunks
// in the read queue).  At this point, the application should call
// Read(). If the read fails for some reason, the m_uMsg is sent
// with wParam set to CWINSOCK_ERROR_READING.
//
LONG CDatagramSocket::HandleRead(WPARAM wParam, LPARAM lParam)
{
  while (1)
  {
    // allocate memory for incoming data
    LPVOID pData = malloc(READ_BUF_LEN);
    LPDATAGRAMDATA pDatagramData = new DATAGRAMDATA;
    if ((pData == NULL) || (pDatagramData == NULL))
    {
      // free anything that was allocated
      if (pData != NULL)
        free(pData);
      pData = NULL;
      if (pDatagramData != NULL)
        delete pDatagramData;
      pDatagramData = NULL;

      // tell the parent that a possible data read failed
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);

      // fake the event to try again
      PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
       WSAMAKESELECTREPLY(FD_READ, 0));

      break;
    }

    // receive data
    int nAddrLen = sizeof(SOCKADDR_IN);
    int nBytesRead = recvfrom(m_s, (LPSTR)pData, READ_BUF_LEN, 0,
     (LPSOCKADDR)&(pDatagramData->sin), &nAddrLen);
    if (nBytesRead == SOCKET_ERROR)
    {
      // free memory for incoming data
      free(pData);
      pData = NULL;
      delete pDatagramData;
      pDatagramData = NULL;

      // if the error is just that the read would block,
      // don't do anything; we'll get another FD_READ soon
      m_nLastError = WSAGetLastError();
      if (m_nLastError == WSAEWOULDBLOCK)
        m_nLastError = 0;
      else
        // tell the parent that a data read failed
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);

      break;
    }

    // add the data to the list
    pDatagramData->pData = pData;
    pDatagramData->nLen = nBytesRead;
    TRY
    {
      m_listRead.AddTail(pDatagramData);
    }
    CATCH (CMemoryException, e)
    {
      free(pData);
      pData = NULL;
      delete pDatagramData;
      pDatagramData = NULL;
      // tell the parent that a data read failed
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
      break;
    }
    END_CATCH

    // tell the parent that data has been read
    m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_READING,
     (LPARAM)m_listRead.GetCount());
    
    break;
  }

  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CDatagramSocket::HandleWrite()
//
// Called when there is an asynchronous write event on the socket.
//
// If there is data in the write queue waiting to be sent,
// a WinSock send is attempted.  If the send is successful,
// a m_uMsg message is sent to the application window with
// wParam set to CWINSOCK_DONE_WRITING and lParam set to the
// address of the data that was sent.  On send failure,
// wParam is set to CWINSOCK_ERROR_WRITING and lParam set to
// the address of the data which couldn't be sent.  In either
// case, the application may free the pointer pointing to
// the data or reuse that data buffer.
//
LONG CDatagramSocket::HandleWrite(WPARAM wParam, LPARAM lParam)
{
  while (1)
  {
    // check to see if there is any data to send
    if (m_listWrite.IsEmpty())
      break;

    // get pointers to data, data length, and destination address
    LPDATAGRAMDATA pDatagramData = (LPDATAGRAMDATA)m_listWrite.GetHead();
    LPVOID pData = pDatagramData->pData;
    int nLen = pDatagramData->nLen;
    SOCKADDR_IN sin;
    memcpy(&sin, &(pDatagramData->sin), sizeof(SOCKADDR_IN));

    // send the data
    BOOL bRemove = FALSE;      // remove data from queue?
    int nBytesSent = sendto(m_s, (LPCSTR)pData, nLen, 0,
     (LPSOCKADDR)&sin, sizeof(SOCKADDR_IN));
    if (nBytesSent == SOCKET_ERROR)
    {
      // if the error is just that the send would block,
      // don't do anything; we'll get another FD_WRITE soon
      m_nLastError = WSAGetLastError();
      if (m_nLastError == WSAEWOULDBLOCK)
        m_nLastError = 0;
      else
      {
        bRemove = TRUE;
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
         (LPARAM)pData);
      }
    }
    else
    {
      // if data was sent, we must still check to see
      // if all the bytes were sent
      bRemove = TRUE;
      if (nBytesSent == nLen)
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_WRITING,
         (LPARAM)pData);
      else
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
         (LPARAM)pData);
    }

    // if the data was sent or there was a real
    // error, remove the data from the queue
    if (bRemove)
    {
      delete pDatagramData;
      m_listWrite.RemoveHead();
    }

    // if there is more data to send, trigger this FD_WRITE handler
    if (!m_listWrite.IsEmpty())
      PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
       WSAMAKESELECTREPLY(FD_WRITE, 0));

    break;
  }

  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket constructor
//
// Constructs the CStreamSocket object. Initializes member variables
//
CStreamSocket::CStreamSocket(CWnd *pParentWnd, UINT uMsg)
{
  m_pParentWnd = pParentWnd;
  ASSERT(m_pParentWnd != NULL);
  m_uMsg = uMsg;
  ASSERT(m_uMsg != 0);
  InitVars();
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket destructor
//
CStreamSocket::~CStreamSocket()
{
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::InitVars()
//
// Initialize class member variables.
//
void CStreamSocket::InitVars(BOOL bInitLastError/*= TRUE*/)
{
  if (bInitLastError)
    m_nLastError = 0;

  m_s = INVALID_SOCKET;
  memset(&m_sinLocal, 0, sizeof(SOCKADDR_IN));
  memset(&m_sinRemote, 0, sizeof(SOCKADDR_IN));
  m_bServer = FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock.  Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// port number, in host order, as input.  A port number
// should only be specified if the socket is to be bound
// to a certain port.  If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CStreamSocket::CreateSocket(int nLocalPort)
{
  // if this version of the function is being called,
  // a valid port number must be specified
  if (nLocalPort <= 0)
    return CWINSOCK_PROGRAMMING_ERROR;

  // convert the port number into a string and
  // call the version of CreateSocket() which
  // accepts a string
  char pszLocalService[18];
  _itoa(nLocalPort, pszLocalService, 10);
  return CreateSocket(pszLocalService);
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::CreateSocket()
//
// Create a hidden window that will receive asynchronous messages
// from WinSock.  Also creates a socket and optionally binds it to
// a name if the socket is a server socket.
//
// This version of the CreateSocket() function takes a
// string containing a service name or port number.
// A parameter should only be specified if the socket is to be
// bound to a certain port.  If you don't care which port is
// assigned to the socket, just call CreateSocket() without
// any parameter, causing CreateSocket(NULL) to be called.
//
int CStreamSocket::CreateSocket(LPSTR pszLocalService/*= NULL*/)
{
  int nStatus = CWINSOCK_NOERROR;

  while (1)
  {
    // Make sure the socket isn't already created.
    // If the socket handle is valid, return from this
    // function right away so the existing parameters of
    // the object are not tampered with.
    if (m_s != INVALID_SOCKET)
      return CWINSOCK_PROGRAMMING_ERROR;

    InitVars();

    // create the hidden window
    RECT rect;
    rect.left = 0;
    rect.top = 0;
    rect.right = 100;
    rect.bottom = 100;
    if (Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect, m_pParentWnd, 0) == 0)
    {
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }

    // create the socket
    m_s = socket(PF_INET, SOCK_STREAM, 0);
    if (m_s == INVALID_SOCKET)
    {
      m_nLastError = WSAGetLastError();
      nStatus = CWINSOCK_WINSOCK_ERROR;
      DestroyWindow();
      break;
    }

    // If pszLocalService is not NULL, this is a server socket
    // that will accept data on the specified port.
    if (pszLocalService != NULL)
    {
      // this socket is bound to a port number
      // so set the server flag
      m_bServer = TRUE;

      // assign the address family
      m_sinLocal.sin_family = AF_INET;

      // assign the service port (may have to do a database lookup
      // if a service port number was not specified)
      m_sinLocal.sin_port = htons(atoi(pszLocalService));
      if (m_sinLocal.sin_port == 0)
      {
        LPSERVENT pSent = getservbyname(pszLocalService, "tcp");
        if (pSent == NULL)
        {
          m_nLastError = WSAGetLastError();
          nStatus = CWINSOCK_WINSOCK_ERROR;
          closesocket(m_s);
          DestroyWindow();
          break;
        }
        m_sinLocal.sin_port = pSent->s_port;
      }

      // assign the IP address
      m_sinLocal.sin_addr.s_addr = htonl(INADDR_ANY);

      // bind the server socket to the name containing the port
      if (bind(m_s, (LPSOCKADDR)&m_sinLocal, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
      {
        m_nLastError = WSAGetLastError();
        nStatus = CWINSOCK_WINSOCK_ERROR;
        closesocket(m_s);
        DestroyWindow();
        break;
      }
    }

    // start asynchronous event notification
    long lEvent;
    if (m_bServer)
      lEvent = FD_READ | FD_WRITE | FD_ACCEPT | FD_CLOSE;
    else
      lEvent = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;

	TRACE("WSAAsyncSelect in CreateSocket\n");

    if (WSAAsyncSelect(m_s, m_hWnd, CWINSOCK_EVENT_NOTIFICATION, lEvent) ==
     SOCKET_ERROR)
    {
      m_nLastError = WSAGetLastError();
      nStatus = CWINSOCK_WINSOCK_ERROR;
      closesocket(m_s);
      DestroySocket();
      break;
    }

    // if this is a server, listen for client connections
    if (m_bServer)
    {
      if (listen(m_s, 3) == SOCKET_ERROR)
      {
        m_nLastError = WSAGetLastError();
        nStatus = CWINSOCK_WINSOCK_ERROR;
        closesocket(m_s);
        DestroySocket();
        break;
      }
    }

    break;
  }

  // if anything failed in this function, set the
  // socket variables appropriately
  if (nStatus != CWINSOCK_NOERROR)
    InitVars(FALSE);

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::DestroySocket()
//
// Close the socket, remove any queued data,
// and destroy the hidden window.
//
int CStreamSocket::DestroySocket()
{
  int nStatus = CWINSOCK_NOERROR;

  // make sure the socket is valid
  if (m_s == INVALID_SOCKET)
    nStatus = CWINSOCK_PROGRAMMING_ERROR;
  else
  {
    // remove any data in the write queue
    while (!m_listWrite.IsEmpty())
    {
      LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listWrite.RemoveHead();
      LPVOID pData = pStreamData->pData;
      delete pStreamData;

      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
       (LPARAM)pData);
    }

    // remove any data in the read queue
    while (!m_listRead.IsEmpty())
    {
      LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead();
      free(pStreamData->pData);
      delete pStreamData;
    }

    // close the socket and initialize variables
    closesocket(m_s);
    InitVars();

    // destroy the hidden window
    DestroyWindow();
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Connect()
//
// Connect the client socket to a server specified by the name and port.
//
// This version of the Conncet() function takes a pointer to a
// string representing the host name to send the data to and
// an integer representing the port number to connect to.
//
int CStreamSocket::Connect(LPSTR pszRemoteName, int nRemotePort)
{
  // convert the port number into a string and
  // call the version of Connect() which accepts
  // a string service name or number
  char pszRemoteService[18];
  int CheckRet;
  _itoa(nRemotePort, pszRemoteService, 10);
  CheckRet = Connect(pszRemoteName, pszRemoteService);
  return( CheckRet);
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Connect()
//
// Connect the client socket to a server specified by the name and
// service name or port.
//
// This version of the Connect() function takes a pointer to a
// string representing the host name to send the data to and
// an integer representing the service name or port number to
// connect to.
//
int CStreamSocket::Connect(LPSTR pszRemoteName, LPSTR pszRemoteService)
{
  int nStatus = CWINSOCK_NOERROR; // error status
  LPHOSTENT pHent;                // pointer to host entry structure
  LPSERVENT pSent;                // pointer to service entry structure
  SOCKADDR_IN sinRemote;          // Internet address of destination

  while (1)
  {
    // assign the address family
    sinRemote.sin_family = AF_INET;

    // assign the service port (may have to do a database lookup
    // if a service port number was not specified)
    sinRemote.sin_port = htons(atoi(pszRemoteService));
    if (sinRemote.sin_port == 0)
    {
      pSent = getservbyname(pszRemoteService, "tcp");
      if (pSent == NULL)
      {
        m_nLastError = WSAGetLastError();
	nStatus = CWINSOCK_WINSOCK_ERROR;
	CWinSockErrorBox( m_nLastError, "From within Connect(name,serv) on getservbyname: ");
        break;
      }
      sinRemote.sin_port = pSent->s_port;
    }

    // assign the IP address (may have to do a database lookup
    // if a dotted decimal IP address was not specified)
    sinRemote.sin_addr.s_addr = inet_addr(pszRemoteName);
    if (sinRemote.sin_addr.s_addr == INADDR_NONE)
    {
      pHent = gethostbyname(pszRemoteName);
      if (pHent == NULL)
      {
        m_nLastError = WSAGetLastError();
        nStatus = CWINSOCK_WINSOCK_ERROR;
	CWinSockErrorBox( m_nLastError, "From within Connect(name,serv) after gethostbyname: ");
        break;
      }
      sinRemote.sin_addr.s_addr = *(u_long *)pHent->h_addr;
    }

    // call the version of Connect() that takes an
    // Internet address structure
    return Connect(&sinRemote);
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Connect()
//
// Connect the client socket to a server specified by the
// Internet address.
//
// This version of the Connect() function takes a pointer
// to an Internet address structure to connect to.
//
int CStreamSocket::Connect(LPSOCKADDR_IN psinRemote)
{
  int nStatus = CWINSOCK_NOERROR;
  
  while (1)
  {
    // only clients should call connect
    if (m_bServer)
    {
      nStatus = CWINSOCK_PROGRAMMING_ERROR;
      break;
    }

    // copy the Internet address of the remote server to connect to
    memcpy(&m_sinRemote, psinRemote, sizeof(SOCKADDR_IN));

    // attempt the asynchronous connect
    if (connect(m_s, (LPSOCKADDR)&m_sinRemote, sizeof(SOCKADDR_IN)) ==
     SOCKET_ERROR)
    {
      m_nLastError = WSAGetLastError();


      if (m_nLastError == WSAEWOULDBLOCK){
		  //    CWinSockErrorBox( m_nLastError, "From within Connect(address) after asynch connect: ");
		  TRACE( "WSAEWOULDBLOCK From within Connect(address) after asynch connect\n");

		  m_nLastError = 0;
      } else {
		  nStatus = CWINSOCK_WINSOCK_ERROR;
		  break;
      }

	}

	break;
  }
  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Accept()
//
// Accept a connection request from a client.
//
// This function takes a pointer to a CStreamSocket object. This
// pointer will become the newly connected socket.
//
int CStreamSocket::Accept(CStreamSocket *pStreamSocket)
{
  int nStatus = CWINSOCK_NOERROR;

  while (1)
  {
    // must have valid CStreamSocket object pointer passed in
    if (pStreamSocket == NULL)
    {
      ASSERT(0);
      nStatus = CWINSOCK_PROGRAMMING_ERROR;
      break;
    }

    // only servers should call accept
    if (!m_bServer)
    {
      nStatus = CWINSOCK_PROGRAMMING_ERROR;
      break;
    }

    // Make sure the socket isn't already created.
    // If the socket handle is valid, return from this
    // function right away so the existing parameters of
    // the object are not tampered with.
    if (pStreamSocket->m_s != INVALID_SOCKET)
      return CWINSOCK_PROGRAMMING_ERROR;

    pStreamSocket->InitVars();

    // create the hidden window
    RECT rect;
    rect.left = 0;
    rect.top = 0;
    rect.right = 100;
    rect.bottom = 100;
    if (pStreamSocket->Create(NULL, NULL, WS_OVERLAPPEDWINDOW, rect,
     pStreamSocket->m_pParentWnd, 0) == 0)
    {
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }

    // accept the client connection
    pStreamSocket->m_s = accept(m_s, NULL, NULL);
    if (pStreamSocket->m_s == INVALID_SOCKET)
    {
      m_nLastError = WSAGetLastError();
      nStatus = CWINSOCK_WINSOCK_ERROR;
      pStreamSocket->DestroyWindow();
      break;
    }

    // start asynchronous event notification
    long lEvent;
    lEvent = FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE;
	TRACE("WSAAsyncSelect in Accept\n");
    if (WSAAsyncSelect(pStreamSocket->m_s, pStreamSocket->m_hWnd,
     CWINSOCK_EVENT_NOTIFICATION, lEvent) == SOCKET_ERROR)
    {
      m_nLastError = WSAGetLastError();
      nStatus = CWINSOCK_WINSOCK_ERROR;
      closesocket(pStreamSocket->m_s);
      pStreamSocket->DestroySocket();
      break;
    }

    break;
  }

  // if anything failed in this function, set the
  // socket variables appropriately
  if (nStatus == CWINSOCK_WINSOCK_ERROR)
    pStreamSocket->InitVars(FALSE);
  else if (nStatus == CWINSOCK_NOERROR)
    // notify the parent if the connection was accepted successfully
    pStreamSocket->m_pParentWnd->PostMessage(pStreamSocket->m_uMsg,
     CWINSOCK_YOU_ARE_CONNECTED);

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Write()
//
// Write data to the socket..
//
// This function takes an integer representing the length of the
// data to send and a pointer to the data to send.
//
// The data pointed to by pData must remain valid until either
// the Write() function returns with an error, or the
// write's completion is notified by the m_uMsg being sent
// to the window that owns this datagram object with wParam set
// to CWINSOCK_DONE_WRITING or CWINSOCK_ERROR_WRITING.
//
int CStreamSocket::Write(int nLen, LPVOID pData)
{
  int nStatus = CWINSOCK_NOERROR;

  while (1)
  {
    // dynamically allocate a structure to hold the
    // data pointer and the data's length
    LPSTREAMDATA pStreamData = new STREAMDATA;
    if (pStreamData == NULL)
    {
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }
    pStreamData->pData = pData;
    pStreamData->nLen = nLen;

    // add the data to the list
    TRY
    {
      m_listWrite.AddTail(pStreamData);
    }
    CATCH (CMemoryException, e)
    {
      delete pStreamData;
      nStatus = CWINSOCK_WINDOWS_ERROR;
      break;
    }
    END_CATCH

    // trigger the FD_WRITE handler to try to send
    PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s, WSAMAKESELECTREPLY(FD_WRITE, 0));
    break;
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::Read()
//
// Read data that has been received by the socket.
//
// This function takes a pointer to an integer that will be filled
// with the length of the data read.
// 
// A pointer to the data is returned on success.  The application
// using this object must free this pointer.  NULL is returned on failure.
//
LPVOID CStreamSocket::Read(LPINT pnLen)
{
  LPVOID pData = NULL;

  // check to see if there is data to retrieve
  if (!m_listRead.IsEmpty())
  {
    // remove the stream data from the list
    LPSTREAMDATA pStreamData = (LPSTREAMDATA)m_listRead.RemoveHead();
    pData = pStreamData->pData;
    *pnLen = pStreamData->nLen;
    delete pStreamData;
  }

  return pData;
}

// message map
BEGIN_MESSAGE_MAP(CStreamSocket, CWnd)
  //{{AFX_MSG_MAP(CStreamSocket)
  //}}AFX_MSG_MAP
  ON_MESSAGE(CWINSOCK_EVENT_NOTIFICATION, OnWinSockEvent)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::OnWinSockEvent()
//
// Called when there is an asynchronous event on the socket.
//
LONG CStreamSocket::OnWinSockEvent(WPARAM wParam, LPARAM lParam)
{
  TRACE("OnWinSockEvent: ");
  // check for an error
  if (WSAGETSELECTERROR(lParam) != 0)
    return 0L;

  // what event are we being notified of?
  switch (WSAGETSELECTEVENT(lParam))
  {
    case FD_READ:
		TRACE("FD_READ\n");
      return HandleRead(wParam, lParam);
      break;
    case FD_WRITE:
	  TRACE("FD_WRITE\n");
      return HandleWrite(wParam, lParam);
      break;
    case FD_ACCEPT:
      TRACE("FD_ACCEPT\n");
      // tell the parent window that a client would like to connect
      // to the server socket
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_READY_TO_ACCEPT_CONNECTION);
      break;
    case FD_CONNECT:
	  TRACE("FD_CONNECT\n");
      // tell the parent window that the socket has connected
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_YOU_ARE_CONNECTED);
      break;
    case FD_CLOSE:
	  TRACE("FD_CLOSE\n");
      // check for more data queued on the socket
      // (don't tell the application that the socket is closed
      // until all data has been read and notification has been posted)
	  TRACE("About to HandleRead in cwinsock.cpp\n");
      if (HandleRead(wParam, lParam))
      {
        // fake the close event to try again
        PostMessage(CWINSOCK_EVENT_NOTIFICATION, wParam, lParam);
        break;
      }

      // tell the parent window that the socket is closed
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_LOST_CONNECTION);
      break;
    default:
      // this should never happen
      ASSERT(0);
      break;
  }

  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::HandleRead()
//
// Called when there is an asynchronous read event on the socket.
//
// If the read was successful, the data and its length are stored
// in the read queue.  Upon a successful read, the application
// window using this object is then notified with the m_uMsg message
// (wParam set to CWINSOCK_DONE_READING; lParam set to the number of
// data chunks in the read queue).  At this point, the application
// should call Read(). If the read fails for some reason, the m_uMsg
// is sent with wParam set to CWINSOCK_ERROR_READING.
//
LONG CStreamSocket::HandleRead(WPARAM wParam, LPARAM lParam)
{
  while (1)
  {
    // allocate memory for incoming data
    LPVOID pData = malloc(READ_BUF_LEN);
    LPSTREAMDATA pStreamData = new STREAMDATA;
    if ((pData == NULL) || (pStreamData == NULL))
    {
      // free anything that was allocated
      if (pData != NULL)
        free(pData);
      pData = NULL;
      if (pStreamData != NULL)
        delete pStreamData;
      pStreamData = NULL;

      // tell the parent that a possible data read failed
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);

      // fake the event to try again
      PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
       WSAMAKESELECTREPLY(FD_READ, 0));

      break;
    }

    // receive data
    int nBytesRead = recv(m_s, (LPSTR)pData, READ_BUF_LEN, 0);
    if (nBytesRead == SOCKET_ERROR)
    {
      // free memory for incoming data
      free(pData);
      pData = NULL;
      delete pStreamData;
      pStreamData = NULL;

      // if the error is just that the read would block,
      // don't do anything; we'll get another FD_READ soon
      m_nLastError = WSAGetLastError();
      if (m_nLastError == WSAEWOULDBLOCK)
        m_nLastError = 0;
      else
        // tell the parent that a data read failed
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);

      break;
    }

    // make sure some data was read
    if (nBytesRead == 0)
    {
      // free memory for incoming data
      free(pData);
      pData = NULL;
      delete pStreamData;
      pStreamData = NULL;

      break;
    }

    // add the data to the list
    pStreamData->pData = pData;
    pStreamData->nLen = nBytesRead;
    TRY
    {
      m_listRead.AddTail(pStreamData);
    }
    CATCH (CMemoryException, e)
    {
      free(pData);
      pData = NULL;
      delete pStreamData;
      pStreamData = NULL;
      // tell the parent that a data read failed
      m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_READING);
      break;
    }
    END_CATCH
  

    // tell the parent that data has been read
    m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_READING,
     (LPARAM)m_listRead.GetCount());

    // 1 is returned if there is data so CStreamSocket::OnWinSockEvent()'s
    // FD_CLOSE handler will know when the socket can really be closed
    return 1L;
    
    break;
   }

  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::HandleWrite()
//
// Called when there is an asynchronous write event on the socket.
//
// If there is data in the write queue waiting to be sent,
// a WinSock send is attempted.  If the send is successful,
// a m_uMsg message is sent to the application window with
// wParam set to CWINSOCK_DONE_WRITING and lParam set to the
// address of the data that was sent.  On send failure,
// wParam is set to CWINSOCK_ERROR_WRITING and lParam set to
// the address of the data which couldn't be sent.  In either
// case, the application may free the pointer pointing to
// the data or reuse that data buffer.  It is possible for the
// entire amount of data to not be sent in one call to send().
// In this case, an attempt is made to send the remaining portion
// of that block of data the next time HandleWrite() is invoked.
// 
//
LONG CStreamSocket::HandleWrite(WPARAM wParam, LPARAM lParam)
{
  LPSTREAMDATA pStreamData;            // pointer to stream data structure
  LPVOID pData;                        // pointer to buffer to send
  int nLen;                            // total length of buffer to send
  static LPVOID pDataRemaining = NULL; // pointer into buffer to send
  static int nLenRemaining = 0;        // number of bytes left to send

  while (1)
  {
    // check to see if there is any data to send
    if (m_listWrite.IsEmpty())
      break;

    // if we are not in the middle of another buffer send,
    // get data and data length from the write queue
    pStreamData = (LPSTREAMDATA)m_listWrite.GetHead(); // not RemoveHead()
    pData = pStreamData->pData;
    nLen = pStreamData->nLen;
    if (pDataRemaining == NULL)
    {
      pDataRemaining = pData;
      nLenRemaining = nLen;
    }

    // send the data
    BOOL bRemove = FALSE;      // remove data from queue?
    int nBytesSent = send(m_s, (LPCSTR)pDataRemaining, nLenRemaining, 0);
    if (nBytesSent == SOCKET_ERROR)
    {
      // if the error is just that the send would block,
      // don't do anything; we'll get another FD_WRITE soon
      m_nLastError = WSAGetLastError();
      if (m_nLastError == WSAEWOULDBLOCK)
        m_nLastError = 0;
      else
      {
        bRemove = TRUE;
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_ERROR_WRITING,
         (LPARAM)pData);
      }
    }
    else
    {
      // if data was sent, we must still check to see
      // if all the bytes were sent
      if (nBytesSent == nLenRemaining)
      {
        bRemove = TRUE;
        m_pParentWnd->PostMessage(m_uMsg, CWINSOCK_DONE_WRITING,
         (LPARAM)pData);
      }
      else
      {
        // the complete buffer was not sent so adjust
        // these values accordingly
        pDataRemaining = (LPVOID)((LPCSTR)pDataRemaining + nBytesSent);
        nLenRemaining = nLenRemaining - nBytesSent;
      }
    }

    // if the data was completely sent or there was
    // a real error, remove the data from the queue
    if (bRemove)
    {
      delete pStreamData;
      m_listWrite.RemoveHead();
      pDataRemaining = NULL;
      nLenRemaining = 0;
    }

    // if there is more data to send, trigger this FD_WRITE handler
    if (!m_listWrite.IsEmpty())
      PostMessage(CWINSOCK_EVENT_NOTIFICATION, m_s,
       WSAMAKESELECTREPLY(FD_WRITE, 0));

    break;
  }

  return 0L;
}

/////////////////////////////////////////////////////////////////////////////
// CStreamSocket::GetPeerName()
//
// Copies the Internet address of the other end of the socket
// connection into the pointer provided.
// Useful for server's to use after an Accept().
//
int CStreamSocket::GetPeerName(LPSOCKADDR_IN psinRemote)
{
  int nStatus = CWINSOCK_NOERROR;
  int nLen = sizeof(SOCKADDR_IN);

  // make sure the listening socket doesn't call this function
  if (m_bServer)
    nStatus = CWINSOCK_PROGRAMMING_ERROR;
  else if (getpeername(m_s, (LPSOCKADDR)psinRemote, &nLen) == SOCKET_ERROR)
  {
    m_nLastError = WSAGetLastError();
    nStatus = CWINSOCK_WINSOCK_ERROR;
  }

  return nStatus;
}

/////////////////////////////////////////////////////////////////////////////
// Utility functions
/////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////
// CWinSockErrorBox
//
void CWinSockErrorBox(int nError, LPSTR pszMessage/*= NULL*/)
{
#define ERROR_BUF_LEN (1000)
  char pszError[ERROR_BUF_LEN];

  wsprintf(pszError, "WinSock error %d: ", nError);

  switch (nError)
  {
    case WSAEINTR:
      lstrcat(pszError, "Interrupted system call");
      break;
    case WSAEBADF:
      lstrcat(pszError, "Bad file number");
      break;
    case WSAEACCES:
      lstrcat(pszError, "Permission denied");
      break;
    case WSAEFAULT:
      lstrcat(pszError, "Bad address");
      break;
    case WSAEINVAL:
      lstrcat(pszError, "Invalid argument");
      break;
    case WSAEMFILE:
      lstrcat(pszError, "Too many open files");
      break;
    case WSAEWOULDBLOCK:
      lstrcat(pszError, "Operation would block");
      break;
    case WSAEINPROGRESS:
      lstrcat(pszError, "Operation now in progress");
      break;
    case WSAEALREADY:
      lstrcat(pszError, "Operation already in progress");
      break;
    case WSAENOTSOCK:
      lstrcat(pszError, "Socket operation on non-socket");
      break;
    case WSAEDESTADDRREQ:
      lstrcat(pszError, "Destination address required");
      break;
    case WSAEMSGSIZE:
      lstrcat(pszError, "Message too long");
      break;
    case WSAEPROTOTYPE:
      lstrcat(pszError, "Protocol wrong type for socket");
      break;
    case WSAENOPROTOOPT:
      lstrcat(pszError, "Protocol not available");
      break;
    case WSAEPROTONOSUPPORT:
      lstrcat(pszError, "Protocol not supported");
      break;
    case WSAESOCKTNOSUPPORT:
      lstrcat(pszError, "Socket type not supported");
      break;
    case WSAEOPNOTSUPP:
      lstrcat(pszError, "Operation not supported on socket");
      break;
    case WSAEPFNOSUPPORT:
      lstrcat(pszError, "Protocol family not supported");
      break;
    case WSAEAFNOSUPPORT:
      lstrcat(pszError, "Address family not supported by protocol family");
      break;
    case WSAEADDRINUSE:
      lstrcat(pszError, "Address already in use");
      break;
    case WSAEADDRNOTAVAIL:
      lstrcat(pszError, "Can't assign requested address");
      break;
    case WSAENETDOWN:
      lstrcat(pszError, "Network is down");
      break;
    case WSAENETUNREACH:
      lstrcat(pszError, "Network is unreachable");
      break;
    case WSAENETRESET:
      lstrcat(pszError, "Network dropped connection on reset");
      break;
    case WSAECONNABORTED:
      lstrcat(pszError, "Software caused connection abort");
      break;
    case WSAECONNRESET:
      lstrcat(pszError, "Connection reset by peer");
      break;
    case WSAENOBUFS:
      lstrcat(pszError, "No buffer space available");
      break;
    case WSAEISCONN:
      lstrcat(pszError, "Socket is already connected");
      break;
    case WSAENOTCONN:
      lstrcat(pszError, "Socket is not connected");
      break;
    case WSAESHUTDOWN:
      lstrcat(pszError, "Can't send after socket shutdown");
      break;
    case WSAETOOMANYREFS:
      lstrcat(pszError, "Too many references: can't splice");
      break;
    case WSAETIMEDOUT:
      lstrcat(pszError, "Connection timed out");
      break;
    case WSAECONNREFUSED:
      lstrcat(pszError, "Connection refused");
      break;
    case WSAELOOP:
      lstrcat(pszError, "Too many levels of symbolic links");
      break;
    case WSAENAMETOOLONG:
      lstrcat(pszError, "File name too long");
      break;
    case WSAEHOSTDOWN:
      lstrcat(pszError, "Host is down");
      break;
    case WSAEHOSTUNREACH:
      lstrcat(pszError, "No route to host");
      break;
    case WSAENOTEMPTY:
      lstrcat(pszError, "Directory not empty");
      break;
    case WSAEPROCLIM:
      lstrcat(pszError, "Too many processes");
      break;
    case WSAEUSERS:
      lstrcat(pszError, "Too many users");
      break;
    case WSAEDQUOT:
      lstrcat(pszError, "Disc quota exceeded");
      break;
    case WSAESTALE:
      lstrcat(pszError, "Stale NFS file handle");
      break;
    case WSAEREMOTE:
      lstrcat(pszError, "Too many levels of remote in path");
      break;
#ifdef _WIN32
    case WSAEDISCON:
      lstrcat(pszError, "Disconnect");
      break;
#endif
    case WSASYSNOTREADY:
      lstrcat(pszError, "Network sub-system is unusable");
      break;
    case WSAVERNOTSUPPORTED:
      lstrcat(pszError, "WinSock DLL cannot support this application");
      break;
    case WSANOTINITIALISED:
      lstrcat(pszError, "WinSock not initialized");
      break;
    case WSAHOST_NOT_FOUND:
      lstrcat(pszError, "Host not found");
      break;
    case WSATRY_AGAIN:
      lstrcat(pszError, "Non-authoritative host not found");
      break;
    case WSANO_RECOVERY:
      lstrcat(pszError, "Non-recoverable error");
      break;
    case WSANO_DATA:
      lstrcat(pszError, "Valid name, no data record of requested type");
      break;
    default:
      lstrcpy(pszError, "Not a WinSock error");
      break;
  }

  lstrcat(pszError, "\n");

  int n = lstrlen(pszError);
  if (pszMessage != NULL)
    n += lstrlen(pszMessage);
  if ((pszMessage != NULL) && (n < ERROR_BUF_LEN))
    lstrcat(pszError, pszMessage);

//    MessageBox( NULL, pszError, "Ksign, winsock", MB_OK);

  AfxMessageBox(pszError);
}
