/***************************************************************************************
****************************************************************************************

 class cSocket

 Description:
 A universal asynchronous bidirectional TCP Winsock Socket class for client and server.
 The server supports up to 62 connections at the same time.
 This class may run within one single thread.

 All functions in this class return an API error code or 0 on success.

 Author:
 Elm?(www.netcult.ch/elmue)

****************************************************************************************
****************************************************************************************/

#include "stdafx.h"
#include "Socket.h"

/*
----------------------------------------------------------------------------------
Using these conventions results in better readable code and less coding errors !
----------------------------------------------------------------------------------

     cName  for generic class definitions
     CName  for MFC     class definitions
     tName  for type    definitions
     eName  for enum    definitions
     kName  for struct  definitions

    e_Name  for enum variables
    E_Name  for enum constant values

    i_Name  for instances of classes
    h_Name  for handles

    M_Name  for macros
    T_Name  for Templates
    t_Name  for TCHAR or LPTSTR

    s_Name  for strings
   sa_Name  for Ascii strings
   sw_Name  for Wide (Unicode) strings
   bs_Name  for BSTR
    f_Name  for function pointers
    k_Name  for contructs (struct)

    b_Name  bool,BOOL 1 Bit

   s8_Name    signed  8 Bit (char)
  s16_Name    signed 16 Bit (SHORT)
  s32_Name    signed 32 Bit (LONG, int)
  s64_Name    signed 64 Bit (LONGLONG)

   u8_Name  unsigned  8 Bit (BYTE)
  u16_Name  unsigned 16 bit (WORD, WCHAR)
  u32_Name  unsigned 32 Bit (DWORD, UINT)
  u64_Name  unsigned 64 Bit (ULONGLONG)

    d_Name  for double

  ----------------

    m_Name  for member variables of a class (e.g. ms32_Name for int member variable)
    g_Name  for global (static) variables   (e.g. gu16_Name for global WORD)
    p_Name  for pointer                     (e.g.   ps_Name  *pointer to string)
   pp_Name  for pointer to pointer          (e.g.  ppd_Name **pointer to double)
*/


TCP::cSocket::cSocket()
{
 mb_Initialized    = FALSE;
 mu32_WaitIndex    = 0;
 ms8_ReadBuffer    = 0;
 mu32_Tick64Lo     = 0;
 mu32_Tick64Hi     = 0;
 ms64_MaxIdleTime  = 0;
 mu32_EventTimeout = 0;
}

TCP::cSocket::~cSocket()
{
 if (mb_Initialized)
 {
  Close();
  WSACleanup();
 }

 if (ms8_ReadBuffer) delete ms8_ReadBuffer;
}

// protected
// Load ws2_32.dll and initialize Windsock 2.0
DWORD TCP::cSocket::Initialize()
{
 if (mb_Initialized)
  return 0;

 // Winsock version 2.0 is available on ALL Windows operating systems
 // except Windows 95 which comes with Winsock 1.1
 WSADATA k_Data;
 DWORD u32_Error = WSAStartup(MAKEWORD(2,0), &k_Data);
 if (u32_Error)
  return u32_Error;

 ms8_ReadBuffer = new char[READ_BUFFER_SIZE];
 mb_Initialized = TRUE;
 return 0;
}

// Closes all open sockets
DWORD TCP::cSocket::Close()
{
 if (!mi_List.mu32_Count)
  return WSAENOTCONN; // no socket open

 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("Close()");
 #endif
  
 // Request thread safe access to mi_List
 cLock i_Lock;
 DWORD u32_Error = i_Lock.Request(&mk_Lock);
 if (u32_Error)
  return u32_Error;

 mi_List.RemoveAll();
 return 0;
}

// returns the current state of the socket
TCP::cSocket::eState TCP::cSocket::GetState()
{
 return mi_List.me_State;
}

// Get the count of open sockets
DWORD TCP::cSocket::GetSocketCount()
{
 return mi_List.mu32_Count;
}

// returns all open sockets: Key = socket handle, Value = peer IP
DWORD TCP::cSocket::GetAllConnectedSockets(cHash<SOCKET,DWORD>* pi_SockList)
{
 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("GetAllConnectedSockets()");
 #endif

 pi_SockList->Clear();

 // Request thread safe access to mi_List
 cLock i_Lock;
 DWORD u32_Error = i_Lock.Request(&mk_Lock);
 if (u32_Error)
  return u32_Error;

 for (DWORD i=0; i<mi_List.mu32_Count; i++)
 {
  // On a server do not return socket[0] which is never connected
  if (!mi_List.mk_Data[i].u32_IP)
   continue;

  if (mi_List.mk_Data[i].b_Shutdown || mi_List.mk_Data[i].b_Closed)
   continue;

  pi_SockList->Append(mi_List.mk_Data[i].h_Socket, mi_List.mk_Data[i].u32_IP);
 }

 #if TRACE_EVENTS
  TraceA("Returning list with %d connected Sockets", pi_SockList->GetCount());
 #endif
 return 0;
}

// protected
// Create a new unbound socket and add it to mi_List at index 0
DWORD TCP::cSocket::CreateSocket()
{
 DWORD u32_Error = Initialize();
 if (u32_Error)
  return u32_Error;

 if (mi_List.mu32_Count)
  return WSAEISCONN; // Socket already created

 SOCKET h_Socket = socket(AF_INET, SOCK_STREAM, 0);
 if (h_Socket == INVALID_SOCKET)
  return WSAGetLastError();

 HANDLE h_Event = WSACreateEvent();
 if (h_Event == WSA_INVALID_EVENT)
 {
  u32_Error = WSAGetLastError();
  closesocket(h_Socket);
  return u32_Error;
 }

 // Monitor all events on the socket
 if (WSAEventSelect(h_Socket, h_Event, FD_ALL_EVENTS) == SOCKET_ERROR)
 {
  u32_Error = WSAGetLastError();
  closesocket  (h_Socket);
  WSACloseEvent(h_Event);
  return u32_Error;
 }

 mi_List.Add(h_Socket, h_Event);
 return 0;
}

// Creates a Server socket
// You must wait for FD_ACCEPT events before sending data
// u32_BindIP = 0          --> listen on all network adapters
// u32_BindIP = 10.1.0.143 --> listen only on the network adapter with local IP 10.1.0.143
// u32_BindIP = 10.1.2.208 --> listen only on the network adapter with local IP 10.1.2.208
// u32_EventTimeout = the timeout after which ProcessEvents() will abort waiting for an event
// If u32_MaxIdleTime > 0  --> automatically disconnect clients which are idle for a longer time (in seconds)
DWORD TCP::cSocket::Listen(DWORD u32_BindIP, USHORT u16_Port, DWORD u32_EventTimeout, DWORD u32_MaxIdleTime)
{
 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("Listen()");
 #endif

 // Create a server socket which waits for Accept events
 // This socket itself will never be connected to any client!
 DWORD u32_Error = CreateSocket();
 if (u32_Error)
  return u32_Error;

 // get the new socket's data structure which has been filled in CreateSocket()
 kData* pk_Data = &mi_List.mk_Data[0];

 SOCKADDR_IN k_Addr;
 k_Addr.sin_family      = AF_INET;
 k_Addr.sin_addr.s_addr = u32_BindIP;
 k_Addr.sin_port        = htons(u16_Port);

 // Bind the socket to the given port
 if (bind(pk_Data->h_Socket, (LPSOCKADDR)&k_Addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
 {
  u32_Error = WSAGetLastError();
  mi_List.RemoveAll();
  return u32_Error;
 }

 // Start listening for connection requests
 if (listen(pk_Data->h_Socket, WSA_MAXIMUM_WAIT_EVENTS) == SOCKET_ERROR)
 {
  u32_Error = WSAGetLastError();
  mi_List.RemoveAll();
  return u32_Error;
 }

 // The server is not yet connected (wait for FD_ACCEPT!)
 mi_List.me_State  = E_Server;
 ms64_MaxIdleTime  = u32_MaxIdleTime * 1000; // seconds -> ms
 mu32_EventTimeout = u32_EventTimeout; // ms
 return 0;
}

// Creates a Client socket
// u32_ServIP = 0x6401a8c0 -> 192.168.1.100
// If u32_MaxIdleTime > 0  -> automatically disconnect from server if idle for a longer time (in seconds)
// u32_EventTimeout = the timeout after which ProcessEvents() will abort waiting for an event
// *************************** ATTENTION ************************************
// When this funcion returns without error the socket is NOT YET connected!
// You must wait for the FD_CONNECT event before sending data to this socket!
// *************************** ATTENTION ************************************
DWORD TCP::cSocket::ConnectTo(DWORD u32_ServIP, USHORT u16_Port, DWORD u32_EventTimeout, DWORD u32_MaxIdleTime)
{
 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("ConnectTo()");
 #endif

 // Create a client socket which will connect to the server
 DWORD u32_Error = CreateSocket();
 if (u32_Error)
  return u32_Error;

 // get the new socket's data structure which has been filled in CreateSocket()
 kData* pk_Data = &mi_List.mk_Data[0];

 SOCKADDR_IN k_Addr;
 k_Addr.sin_family      = AF_INET;
 k_Addr.sin_addr.s_addr = u32_ServIP;
 k_Addr.sin_port        = htons(u16_Port);

 // Connect the socket to the given IP and port
 if (connect(pk_Data->h_Socket, (LPSOCKADDR)&k_Addr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
 {
  u32_Error = WSAGetLastError();
  if (u32_Error != WSAEWOULDBLOCK)
  {
   mi_List.RemoveAll();
   return u32_Error;
  }
 }

 // The client is not yet connected (wait for FD_CONNECT!)
 mi_List.me_State  = E_Client;
 ms64_MaxIdleTime  = u32_MaxIdleTime * 1000; // seconds -> ms
 mu32_EventTimeout = u32_EventTimeout; // ms

 // s64_IdleSince MUST be set HERE otherwise the socket will be closed before FD_CONNECT is received
 pk_Data->s64_IdleSince = GetTickCount64();
 pk_Data->u32_IP = u32_ServIP;
 return 0;
}

// This function is for use on a server only
// It can be called to force a disconnect of a specific client from the server
DWORD TCP::cSocket::DisconnectClient(SOCKET h_Socket)
{
 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("DisconnectClient(%X)", h_Socket);
 #endif

 // Request thread safe access to mi_List
 cLock i_Lock;
 DWORD u32_Error = i_Lock.Request(&mk_Lock);
 if (u32_Error)
  return u32_Error;
 
 // On a server Socket 0 is not connected
 int s32_Index = mi_List.FindSocket(h_Socket);
 if (s32_Index < 1)
  return ERROR_INVALID_PARAMETER;

 // Ignore all events on this socket except FD_CLOSE
 mi_List.mk_Data[s32_Index].b_Shutdown = TRUE;

 // DO NOT call closesocket() here !!!
 // shutdown() --> FD_CLOSE --> b_Shutdown=TRUE --> cList::Remove() --> closesocket()
 shutdown(h_Socket, SD_BOTH);
 return 0;
}

// Waits for incoming events on the port and processes them (used on Server + Client)
// returns the event(s) that occurred and the socket and it's IP-address which has caused the event.
// If the event is FD_READ the data will be returned in ppi_RecvMem which MUST be cleared with DeleteLeft() by the caller.
// If the event is FD_WRITE the remaining data in the send buffer will be sent and pu32_Sent receives the bytes sent.
// If there is more data to be read or sent, the next call to ProcessEvents() will process the next block of data.
// returns ERROR_TIMEOUT if during the given timeout no event occurres
// pu32_IP = 0x6401a8c0 -> 192.168.1.100
DWORD TCP::cSocket::ProcessEvents(DWORD*   pu32_Events,  // OUT
                                  DWORD*   pu32_IP,      // OUT
          SOCKET*    ph_Socket,  // OUT
                                  cMemory** ppi_RecvMem, // OUT
                                  DWORD*   pu32_Read,    // OUT
          DWORD*   pu32_Sent)    // OUT
{
 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("Entering ProcessEvents()");
 #endif

 DWORD u32_Error;
 kData* pk_Data;
 WSANETWORKEVENTS k_Events;

 *ph_Socket   = 0;
 *pu32_Events = 0;
 *pu32_IP     = 0;
 *pu32_Read   = 0;
 *pu32_Sent   = 0;
 *ppi_RecvMem = 0;

 // Block here if SendTo() or Close() have requested thread safe access to manipulate mi_List.
 cLock i_Lock;
 u32_Error = i_Lock.Loop(&mk_Lock);
 if (u32_Error)
  return u32_Error;

 // Remove all sockets which had a FD_CLOSE event in the last round
 mi_List.RemoveClosed();

 if (!mi_List.mu32_Count)
  return WSAENOTCONN; // No socket open

 // This timer is set to escape from WaitForMultiplEventsEx
 mi_List.mh_Events[0] = mk_Lock.h_ExitTimer;

 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("!!!!! ProcessEvents WAIT");
 #endif

 // Wait until an event occurred or the timeout has elapsed
 // u32_Index is the index in the eventlist mh_Events of the event that has occurred
 DWORD u32_Index = WSAWaitForMultipleEventsEx(mi_List.mu32_Count+1, &mu32_WaitIndex, mi_List.mh_Events, mu32_EventTimeout);
 if (u32_Index == WSA_WAIT_FAILED)
  return WSAGetLastError();

 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("");
  TraceA(">>>>> ProcessEvents CONTINUE");
 #endif

 // -------------------------------------------

 if (u32_Index == WSA_WAIT_TIMEOUT)
 {
  #if TRACE_EVENTS
   TraceA("# WSA_WAIT_TIMEOUT");
  #endif
  u32_Error = ERROR_TIMEOUT;
  goto _Exit;
 }

 // -------------------------------------------

 u32_Index -= WSA_WAIT_EVENT_0;

 // mi_List.mh_Events[0] is used for the Timer. It is not associated with a socket.
 if (u32_Index == 0)
 {
  #if TRACE_EVENTS
   TraceA("# Idle/Request Timer elapsed");
  #endif
  u32_Error = 0; // no error
  goto _Exit;
 }

 // Convert the 1-based event index into the zero-based socket index
 u32_Index--;

 // Get the data associated with the socket that has signaled the event
 pk_Data = &mi_List.mk_Data[u32_Index];

 // -------------------------------------------

 // Get the event(s) that occurred and their associated error array
 if (WSAEnumNetworkEvents(pk_Data->h_Socket, mi_List.mh_Events[u32_Index+1], &k_Events) == SOCKET_ERROR)
 {
  u32_Error = WSAGetLastError();
  mi_List.Remove(u32_Index); // remove socket with problem
  goto _Exit;
 }

 // After shutdown(pk_Data->h_Socket) has been called remove all events except FD_CLOSE
 if (pk_Data->b_Shutdown) k_Events.lNetworkEvents &= FD_CLOSE;

 // Signal the FD_TIMEOUT flag always together with FD_CLOSE
 if (pk_Data->b_Timeout && k_Events.lNetworkEvents & FD_CLOSE)
  k_Events.lNetworkEvents |= FD_TIMEOUT;

 #if TRACE_EVENTS
  char s8_Buf[200];
  FormatEvents(k_Events.lNetworkEvents, s8_Buf);
  TraceA("# Socket %X: Events: %s", pk_Data->h_Socket, s8_Buf);
 #endif

 *pu32_Events = k_Events.lNetworkEvents;

 // -------------------------------------------

 if (k_Events.lNetworkEvents & FD_ACCEPT)
 {
  if (k_Events.iErrorCode[FD_ACCEPT_BIT])
  {
   u32_Error = k_Events.iErrorCode[FD_ACCEPT_BIT];
   goto _Exit;
  }

  SOCKADDR_IN k_Addr;
  int s32_Len = sizeof(k_Addr);
  // Accept the connect request from a client (k_Addr receives peer IP of connecting client)
  // The callback AcceptCondition() checks if the maximum count of connected clients was exceeded
  // If there are already 63 sockets open, the connect request is rejected.
  SOCKET h_Socket = WSAAccept(pk_Data->h_Socket, (LPSOCKADDR)&k_Addr, &s32_Len, AcceptCondition, (DWORD_PTR)this);
  if (h_Socket == INVALID_SOCKET)
  {
   u32_Error = WSAGetLastError();
   goto _Exit;
  }

  HANDLE h_Event = WSACreateEvent();
  if (h_Event == WSA_INVALID_EVENT)
  {
   u32_Error = WSAGetLastError();
   closesocket(h_Socket);
   goto _Exit;
  }
  
  // Monitor events on the newly connected client socket
  if (WSAEventSelect(h_Socket, h_Event, FD_ALL_EVENTS) == SOCKET_ERROR)
  {
   u32_Error = WSAGetLastError();
   closesocket  (h_Socket);
   WSACloseEvent(h_Event);
   goto _Exit;
  }

  // Append the new socket to the socket list
  pk_Data = mi_List.Add(h_Socket, h_Event);
  // Store the client's IP in the socket list
  pk_Data->u32_IP = k_Addr.sin_addr.s_addr;
  // Store the time when the last action was executed on the socket
  pk_Data->s64_IdleSince = GetTickCount64();

  // successfully connected
  if (mi_List.me_State & E_Server)
   mi_List.me_State = (eState)(E_Server | E_Connected);

  // This is a workaround for Windows CE:
  // Without the following code Socket[0] would be dead (deaf) after the first connection.
  // So no further connection could be made after the first connection.
  // This may be a bug but if it is, this bug has never been confirmed by Microsoft.
  // On the other hand Microsoft did not document that this behaviour is normal for Windows CE.
  // So it is an enigma. At least this is a workaround which solves the problem.
  #ifdef _WIN32_WCE
   if (WSAEventSelect(mi_List.mk_Data[0].h_Socket, mi_List.mh_Events[1], FD_ALL_EVENTS) == SOCKET_ERROR)
   {
    u32_Error = WSAGetLastError();
    mi_List.RemoveAll();
    return u32_Error;
   }
  #endif
  
  // Multiple events may be set!
 }

 *ph_Socket = pk_Data->h_Socket;
 *pu32_IP   = pk_Data->u32_IP;
 
 // -------------------------------------------

 if (k_Events.lNetworkEvents & FD_CONNECT)
 {
  if (k_Events.iErrorCode[FD_CONNECT_BIT])
  {
   u32_Error = k_Events.iErrorCode[FD_CONNECT_BIT];
   // The connection has failed -> remove faulty socket from the socket list
   mi_List.Remove(u32_Index);
   goto _Exit;
  }

  // Store the time when the last action was executed on the socket
  pk_Data->s64_IdleSince = GetTickCount64();

  // successfully connected
  // NOTE: on Windows CE a server signals FD_ACCEPT and FD_CONNECT at the same time! (Bug?)
  // So ignore this FD_CONNECT on a Server.
  if (mi_List.me_State & E_Client)
   mi_List.me_State = (eState)(E_Client | E_Connected);
  
  // Multiple events may be set!
 }

 // -------------------------------------------

 if (k_Events.lNetworkEvents & FD_READ)
 {
  if (k_Events.iErrorCode[FD_READ_BIT])
  {
   u32_Error = k_Events.iErrorCode[FD_READ_BIT];
   mi_List.Remove(u32_Index); // remove socket with Read error
   goto _Exit;
  }

  // Read the data into the read buffer
  DWORD u32_Flags = 0;
  WSABUF  k_Buf;
  k_Buf.buf = ms8_ReadBuffer;
  k_Buf.len = READ_BUFFER_SIZE;
  if (WSARecv(pk_Data->h_Socket, &k_Buf, 1, pu32_Read, &u32_Flags, 0, 0) == SOCKET_ERROR)
  {
   u32_Error = WSAGetLastError();

   if (u32_Error == WSAEWOULDBLOCK) u32_Error = 0;
   if (u32_Error)
   { 
    mi_List.Remove(u32_Index); // remove socket with Read error
    goto _Exit;
   }
  }

  #if TRACE_EVENTS
   TraceA("Received %d Bytes", *pu32_Read);
  #endif

  if (!pk_Data->pi_RecvMem)
    pk_Data->pi_RecvMem = new cMemory(MEMORY_INITIAL_SIZE);
  
  pk_Data->pi_RecvMem->Append(ms8_ReadBuffer, *pu32_Read);

  // Store the time when the last action was executed on the socket
  pk_Data->s64_IdleSince = GetTickCount64();

  *ppi_RecvMem = pk_Data->pi_RecvMem;
  
  // Multiple events may be set!
 }

 // -------------------------------------------

 if (k_Events.lNetworkEvents & FD_WRITE)
 {
  if (k_Events.iErrorCode[FD_WRITE_BIT])
  {
   u32_Error = k_Events.iErrorCode[FD_WRITE_BIT];
   mi_List.Remove(u32_Index); // remove socket with Write error
   goto _Exit;
  }

  // Is there pending data in the send buffer waiting to be sent ?
  if (pk_Data->s8_SendBuf)
  {
   // Send as much as possible data from the send buffer
   DWORD u32_Before = pk_Data->u32_SendPos;
   u32_Error = SendDataBlock(pk_Data->h_Socket, pk_Data->s8_SendBuf, &pk_Data->u32_SendPos, pk_Data->u32_SendLen);
   *pu32_Sent = pk_Data->u32_SendPos - u32_Before;

   if (pk_Data->u32_SendPos == pk_Data->u32_SendLen)
   {
    // All data has been sent successfully -> delete the buffer
    delete pk_Data->s8_SendBuf;
    pk_Data->s8_SendBuf = 0;
   }

   if (u32_Error == WSAEWOULDBLOCK) u32_Error = 0;
   if (u32_Error)
   {
    mi_List.Remove(u32_Index); // remove socket with Write error
    goto _Exit;
   }
  }

  // Store the time when the last action was executed on the socket
  pk_Data->s64_IdleSince = GetTickCount64();
  
  // Multiple events may be set!
 }

 // -------------------------------------------

 if (k_Events.lNetworkEvents & FD_CLOSE)
 {
  // ATTENTION: The socket must NOT yet be removed from mi_List!
  // Sometimes a FD_READ comes at the same time as a FD_CLOSE.
  // In this case the read data would be lost.
  // The socket will be removed in mi_List.RemoveClosed() in the next round
  pk_Data->b_Closed = TRUE;

  if (k_Events.iErrorCode[FD_CLOSE_BIT]) // e.g. WSAECONNABORTED
  {
   u32_Error = k_Events.iErrorCode[FD_CLOSE_BIT];
   goto _Exit;
  }
  
  // Multiple events may be set!
 }

 u32_Error = 0; // Success

 // -------------------------------------------

 _Exit:
 // 1.) Shutdown all connected sockets that are idle for longer than mi_List.ms64_MaxIdleTime
 // 2.) Set the timer mk_Lock.h_ExitTimer to fire when the socket with the longest idle time will be due
 // When cLock::Request() is called, WsaWaitForMultipleEventsEx() must stop blocking.
 // Therefore ProcessIdleSockets() must not be called between cLock::Loop() and WsaWaitForMultipleEventsEx()
 // because it sets the same timer!
 DWORD u32_IdleErr = ProcessIdleSockets("ProcessEvents()");
 if (u32_IdleErr)
  return u32_IdleErr;

 return u32_Error;
}

// This is a special Wait function which eliminates a problem of WSAWaitForMultipleEvents:
// WSAWaitForMultipleEvents scans the events in the event array ph_Events from zero on.
// When it finds one that is signaled it stops and retruns it's index.
// This may cause a problem on a server with high load near 100% CPU power:
// When multiple events are signaled always the ones at the begin of the event array will be preferred
// and the events at the end of the array will be in disadvantage.
// To avoid this, this function uses a pointer pu32_Index from which on a signaled event is searched.
// This pointer is incremented until the last event and then starts from the first again.
// So every client on a server has the same priority in being served.
// ph_Events  = array of event handles
// u32_Count  = count of events in array
// pu32_Index = rotating index
DWORD TCP::cSocket::WSAWaitForMultipleEventsEx(DWORD u32_Count, DWORD* pu32_Index, WSAEVENT* ph_Events, DWORD u32_Timeout)
{
 // Here *pu32_Index is at the position where the last time an event has been signaled
 // Search for a signaled event from *pu32_Index +1 upwards
 for (DWORD C=0; C<u32_Count; C++)
 {
  (*pu32_Index)++;

  if (*pu32_Index >= u32_Count)
   *pu32_Index = 0;

  // Check if the event is set (Timeout = 0)
  DWORD u32_Res = WaitForSingleObject(ph_Events[*pu32_Index], 0);
  if (u32_Res == WAIT_OBJECT_0)
   return WSA_WAIT_EVENT_0 + *pu32_Index;
 }

 // There is no event signaled -> this means that the server is not under stress
 // There is no reason to check the events one by one anymore.
 DWORD u32_Res = WSAWaitForMultipleEvents(u32_Count, ph_Events, FALSE, u32_Timeout, FALSE);
 if (u32_Res != WSA_WAIT_FAILED && u32_Res != WSA_WAIT_TIMEOUT)
 {
  *pu32_Index = u32_Res - WSA_WAIT_EVENT_0;
 }
 return u32_Res;
}

// static
// Decides if a connection request from a client is accepted. (Max 63 open sockets possible)
// WSAAccept() will return WSAECONNREFUSED if AcceptCondition() returns CF_REJECT
int WINAPI TCP::cSocket::AcceptCondition(WSABUF* pk_CallerId, WSABUF* pk_CallerData, QOS* pk_SQOS, QOS* pk_GQOS,
           WSABUF* pk_CalleeId, WSABUF* pk_CalleeData, UINT* pu32_Group, DWORD_PTR p_Param)
{
 cSocket* p_This = (cSocket*)p_Param;

 if (p_This->mi_List.mu32_Count >= WSA_MAXIMUM_WAIT_EVENTS-1)
  return CF_REJECT;
 else
  return CF_ACCEPT;
}

// Shutdown all sockets that are idle for longer than ms64_MaxIdleTime milliseconds
// Set timer mk_Lock.h_ExitTimer to fire when the socket with the longest idle time will become due
// s8_Caller = the calling function, only used for debugging (Trace)
DWORD TCP::cSocket::ProcessIdleSockets(char* s8_Caller)
{
 if (!ms64_MaxIdleTime)
  return 0;

 LONGLONG s64_Now    = GetTickCount64(); // current tick count in ms
 LONGLONG s64_Oldest = s64_Now +1;

 for (DWORD i=0; i<mi_List.mu32_Count; i++)
 {
  kData* pk_Data = &mi_List.mk_Data[i];

  if (!pk_Data->u32_IP || pk_Data->b_Shutdown)
   continue; // Ignore socket 0 on a server (IP = 0.0.0.0)

  // Add 100 ms to assure that the max idle time surely has elapsed because
  // GetTickCount() increments in 17 ms steps (clock interrupt) and is not as exact as SetWaitableTimer()
  if (s64_Now - pk_Data->s64_IdleSince + 100 >= ms64_MaxIdleTime)
  {
   #if TRACE_EVENTS
    TraceA("+ Shutting down idle Socket %X", pk_Data->h_Socket);
   #endif

   // Set FD_TIMEOUT flag when FD_CLOSE will be fired
   pk_Data->b_Timeout = TRUE;
   // Ignore all events on this socket except FD_CLOSE
   pk_Data->b_Shutdown = TRUE;

   // DO NOT call closesocket() here !!!
   // shutdown() --> FD_CLOSE --> b_Shutdown=TRUE --> cList::Remove() --> closesocket()
   shutdown(pk_Data->h_Socket, SD_BOTH);
  }
  else if (s64_Oldest > pk_Data->s64_IdleSince)
  {
   // Get the oldest "last activity"
   s64_Oldest = pk_Data->s64_IdleSince;
  }
 }

 // Do not set the timer if there is no socket connected (s64_Oldest == s64_Now +1)
 if (s64_Oldest <= s64_Now)
 {
  DWORD u32_Interval = (DWORD)max(0, ms64_MaxIdleTime - (s64_Now - s64_Oldest));

  // If the event timeout is short there is no need to set the timer
  if (u32_Interval > mu32_EventTimeout)
   return 0;
  
  #if TRACE_EVENTS
   TraceA("+ %s Setting Timer: Idle Interval= %d ms", s8_Caller, u32_Interval);
  #endif

  // k_Interval must be negative value with 100 nano seconds resolution to set a relative time
  LARGE_INTEGER k_Interval;
  k_Interval.QuadPart = (LONGLONG)u32_Interval * -10000;
  
  // mh_Events[0] == mk_Lock.h_ExitTimer
  if (!SetWaitableTimer(mi_List.mh_Events[0], &k_Interval, 0, 0, 0, FALSE))
   return GetLastError();
 }
 return 0;
}

// protected
// Send the remaining data and adjust the pu32_Pos pointer.
// If the data was sent only partially (WSAEWOULDBLOCK) this function must be called again
DWORD TCP::cSocket::SendDataBlock(SOCKET h_Socket, char* s8_Buf, DWORD* pu32_Pos, DWORD u32_Len)
{
 while (*pu32_Pos < u32_Len)
 {
  WSABUF k_Buf;
  k_Buf.buf =  s8_Buf + *pu32_Pos;
  k_Buf.len = u32_Len - *pu32_Pos;
  
  DWORD u32_Sent = 0;
  if (WSASend(h_Socket, &k_Buf, 1, &u32_Sent, 0, 0, 0) == SOCKET_ERROR)
   return WSAGetLastError();

  #if TRACE_EVENTS
   TraceA("SendDataBlock has sent %d Bytes", u32_Sent);
  #endif

  *pu32_Pos += u32_Sent;
 };
 return 0;
}

// Send the data in s8_Buf to the given socket (Server + Client)
// This function will not block.
// If the data cannot be sent immediately, the function returns WSAEWOULDBLOCK and the data is buffered until the next
// FD_WRITE event which will be signaled when the correct time has come to send more data to that socket.
// It is possible that a part of the data is sent immediately and the rest is buffered to be sent after next FD_WRITE.
// If you call this function while a previous Send operation is still pending, the function returns WSA_IO_PENDING.
// In this case try again later!
// ATTENTION:
// When this function returns without error, this does not mean that all data has already arrived at the recipient!
// WinSock uses a transport buffer and the real transmision of the data may take a long time after this function has returned.
DWORD TCP::cSocket::SendTo(SOCKET h_Socket, char* s8_Buf, DWORD u32_Len)
{
 #if TRACE_EVENTS || TRACE_LOCK
  TraceA("SendTo(Socket %X, %d Bytes)", h_Socket, u32_Len);
 #endif

 // Request thread safe access to mi_List
 cLock i_Lock;
 DWORD u32_Error = i_Lock.Request(&mk_Lock);
 if (u32_Error)
  return u32_Error;

 if (!(mi_List.me_State & E_Connected))
  return WSAENOTCONN; // Socket is not connected

 int s32_Index = mi_List.FindSocket(h_Socket);
 if (s32_Index < 0)
  return ERROR_INVALID_PARAMETER; // Invalid Socket handle passed

 kData* pk_Data = &mi_List.mk_Data[s32_Index];
 if (pk_Data->b_Shutdown || pk_Data->b_Closed)
  return WSAESHUTDOWN; // The socket has already been shut down, but mi_List.Remove() has not yet been called

 pk_Data->s64_IdleSince = GetTickCount64();

 // Set Idle Timer
 if (u32_Error = ProcessIdleSockets("SendTo()       "))
  return u32_Error;

 if (pk_Data->s8_SendBuf)
  return WSA_IO_PENDING; // a Send operation is still in progress on this socket

 // Sends as much data as possible at this moment, increases u32_Pos
 DWORD u32_Pos = 0;
 u32_Error = SendDataBlock(h_Socket, s8_Buf, &u32_Pos, u32_Len);

 // Not all the data could be sent right now:
 // The remaining data is copied into a buffer and will be sent when FD_WRITE becomes signaled.
 if (u32_Error == WSAEWOULDBLOCK)
 {
  u32_Len -= u32_Pos;
  s8_Buf  += u32_Pos;

  pk_Data-> s8_SendBuf = new char[u32_Len];
  pk_Data->u32_SendLen = u32_Len;
  pk_Data->u32_SendPos = 0;
  memcpy(pk_Data->s8_SendBuf, s8_Buf, u32_Len);
  return u32_Error;
 }

 if (u32_Error && u32_Error != WSA_IO_PENDING)
  mi_List.Remove(s32_Index); // remove socket with Write error
 
 return u32_Error;
}

// LocalIp = 0x6401a8c0 -> 192.168.1.100
// returns a list of all local IP's on this computer (multiple IP's if multiple network adapters)
// cHash is used as an array with Key= 0, Value= local IP
DWORD TCP::cSocket::GetLocalIPs(cHash<DWORD,DWORD>* pi_IpList)
{
 pi_IpList->Clear();

 DWORD u32_Error = Initialize();
 if (u32_Error)
  return u32_Error;

 char s8_Host[500];
 if (gethostname(s8_Host, sizeof(s8_Host)) == SOCKET_ERROR)
  return WSAGetLastError();
 
 struct hostent* pk_Host = gethostbyname(s8_Host);
 if (!pk_Host)
  return WSAGetLastError();

 for (DWORD i=0; TRUE; i++)
 {
  if (!pk_Host->h_addr_list[i])
   break; // The IP list is zero terminated
 
  pi_IpList->Append(0, *((DWORD*)pk_Host->h_addr_list[i]));
 }

 if (!pi_IpList->GetCount())
  return WSAENETDOWN; // no local IP means no network available

 return 0;
}

// GetTickCount() flows over to zero after 49 days.
// GetTickCount64() runs eternally.
// Why not use GetSystemTimeAsFileTime() ?
// Because the Systemtime jumps if someone adjusts the Windows clock.
// And when daylight saving time becomes winter time it will always jump by one hour.
// The tick counter runs continously and is not affected by any external influence.
LONGLONG TCP::cSocket::GetTickCount64()
{
 DWORD u32_Tick = GetTickCount(); // UNSIGNED!!

 if (u32_Tick < mu32_Tick64Lo)
  mu32_Tick64Hi ++; // Increment once every 49 days 

 mu32_Tick64Lo = u32_Tick;

 return 0x100000000 * mu32_Tick64Hi + u32_Tick;
}

// Pass a buffer of 200 characters!
// Can be used for debugging: TraceA("Events: %s", s8_Buf);
void TCP::cSocket::FormatEvents(DWORD u32_Events, char* s8_Buf)
{
 s8_Buf[0] = 0;
  if (u32_Events & FD_ACCEPT)                   strcat(s8_Buf, "FD_ACCEPT ");
 if (u32_Events & FD_CONNECT)                  strcat(s8_Buf, "FD_CONNECT ");
 if (u32_Events & FD_CLOSE)                    strcat(s8_Buf, "FD_CLOSE ");
 if (u32_Events & FD_TIMEOUT)                  strcat(s8_Buf, "FD_TIMEOUT ");
 if (u32_Events & FD_READ)                     strcat(s8_Buf, "FD_READ ");
 if (u32_Events & FD_WRITE)                    strcat(s8_Buf, "FD_WRITE ");
 if (u32_Events & FD_OOB)                      strcat(s8_Buf, "FD_OOB ");
 if (u32_Events & FD_QOS)                      strcat(s8_Buf, "FD_QOS ");
 if (u32_Events & FD_GROUP_QOS)                strcat(s8_Buf, "FD_GROUP_QOS ");
 if (u32_Events & FD_ROUTING_INTERFACE_CHANGE) strcat(s8_Buf, "FD_ROUTING_INTERFACE_CHANGE ");
 if (u32_Events & FD_ADDRESS_LIST_CHANGE)      strcat(s8_Buf, "FD_ADDRESS_LIST_CHANGE ");

 if (!u32_Events) strcpy(s8_Buf, "----");
}

// static
// Use this to write Debug output to DebugView from www.sysinternals.com
// TraceA("Closing Socket %X", h_Socket);
void TCP::cSocket::TraceA(const char* s8_Format, ...)
{
#if _DEBUG && (TRACE_EVENTS || TRACE_LOCK)

 static DWORD u32_LastTick   = 0;
 static DWORD u32_LastThread = 0;

 DWORD u32_Tick   = GetTickCount();
 DWORD u32_Thread = GetCurrentThreadId();

 char* s8_Delim = "";
 if (u32_LastThread != u32_Thread)
 {
  s8_Delim = "+++++++++++++++++++++++++++++++++++++\r\n";
 }
 else if (u32_Tick - u32_LastTick > 100)
 {
  s8_Delim = "- - - - - - - - - - - - - - - - - - -\r\n";
 }

 u32_LastTick   = u32_Tick;
 u32_LastThread = u32_Thread;

 const int BUF_SIZE = 5000;
 char s8_Buf[BUF_SIZE+1];
 
 sprintf(s8_Buf, "%s{%04d} ", s8_Delim, u32_Thread);
 DWORD u32_Len = strlen(s8_Buf);
 
 va_list  args;
 va_start(args, s8_Format);
 _vsnprintf(s8_Buf+u32_Len, BUF_SIZE-u32_Len, s8_Format, args);
 
 OutputDebugStringA(s8_Buf);
#endif
}

/***************************************************************************************
****************************************************************************************

 embedded class cList

 Description:
 Stores for all open sockets: their Handle, Wait Event, IP address, Write buffer for pending data

 CLIENT:
 - uses only Index=0 which holds the socket that is connect to the server.

 SERVER:
 - uses a socket at Index=0 which only waits for incomming connection requests. (always: mk_Data[0].u32_IP==0)
 - The socket at Index=0 is never connected to any client.
 - Each of the following sockets Index=1,2,3... may be connected to one client.

 Author:
 Elm?(www.netcult.ch/elmue)

****************************************************************************************
****************************************************************************************/

TCP::cSocket::cList::cList()
{
 mu32_Count = 0;
 me_State   = E_Disconnected;
}

TCP::cSocket::cList::~cList()
{
 RemoveAll();
}

TCP::cSocket::kData* TCP::cSocket::cList::Add(SOCKET h_Socket, HANDLE h_Event)
{
 // Store max 63 sockets
 if (mu32_Count >= WSA_MAXIMUM_WAIT_EVENTS-1)
  return 0;

 memset(&mk_Data[mu32_Count], 0, sizeof(kData));

 mk_Data  [mu32_Count].h_Socket = h_Socket;
 // mh_Events[0] is used for the lock. It is not associated with a socket.
 mh_Events[mu32_Count+1] = h_Event;

 return &mk_Data[mu32_Count++];
}

void TCP::cSocket::cList::RemoveAll()
{
 while (mu32_Count)
 {
  Remove(mu32_Count-1);
 }
}

// Remove sockets that have already received a FD_CLOSE
void TCP::cSocket::cList::RemoveClosed()
{
 for (int i=(int)mu32_Count-1; i>=0; i--)
 {
  if (mk_Data[i].b_Closed) Remove(i);
 }
}

BOOL TCP::cSocket::cList::Remove(DWORD u32_Index)
{
 if (u32_Index >= mu32_Count)
  return FALSE;

 shutdown   (mk_Data[u32_Index].h_Socket, SD_BOTH);
 closesocket(mk_Data[u32_Index].h_Socket);

 // mh_Events[0] is used for the lock. It is not associated with a socket.
 WSACloseEvent(mh_Events[u32_Index+1]);

 if (mk_Data[u32_Index].s8_SendBuf) delete mk_Data[u32_Index].s8_SendBuf;
 if (mk_Data[u32_Index].pi_RecvMem) delete mk_Data[u32_Index].pi_RecvMem;

 // Close the gap by copying the last socket to the deleted location
 mu32_Count--;
 mk_Data  [u32_Index]   = mk_Data  [mu32_Count];
 mh_Events[u32_Index+1] = mh_Events[mu32_Count+1];
 
 if (mu32_Count==0)
  me_State = E_Disconnected;

 // remove E_Connected flag from the server (Socket[0] is never connected)
 if (mu32_Count==1 && (me_State & E_Server))
  me_State = E_Server;

 return TRUE;
}

// returns the index of the given socket in the socket list
// returns -1 if not found
int TCP::cSocket::cList::FindSocket(SOCKET h_Socket)
{
 for (DWORD i=0; i<mu32_Count; i++)
 {
  if (mk_Data[i].h_Socket == h_Socket)
   return i;
 }
 return -1;
}

/***************************************************************************************
****************************************************************************************

 embedded struct kLock and class cLock
 
 Description:
 This class is used to mutually lock the access to mi_List if cSocket is running multithreaded.
 cLock must be created on the stack in a function requesting write access to mi_List:
 The function Loop()    locks when entering into the endless loop ProcessEvents()
 The function Request() locks when entering into Close() or SendTo(),
 The destructor of cLock releases the lock when these functions have exited.
 
 ATTENTION:
 A Mutex alone will not work here because ProcessEvents() is an endless loop,
 which re-enters before the thread context has been switched to the other thread!
 
 If multithreading is not in use this class does not block in neither function.
 
 Author:
 Elm?(www.netcult.ch/elmue)

****************************************************************************************
****************************************************************************************/

TCP::cSocket::kLock::kLock()
{
 h_LoopEvent = 0;
 h_ExitTimer = 0;
 h_Mutex     = 0;
}

TCP::cSocket::kLock::~kLock()
{
 CloseHandle(h_LoopEvent);
 CloseHandle(h_ExitTimer);
 CloseHandle(h_Mutex);
}

DWORD TCP::cSocket::kLock::Init()
{
 if (!h_LoopEvent) h_LoopEvent = CreateEvent(0, TRUE,  TRUE,  0); // manual-reset, default= run
 if (!h_LoopEvent) return GetLastError();
 
 if (!h_ExitTimer) h_ExitTimer = CreateWaitableTimer(0, FALSE, 0); // auto-reset,  default= block
 if (!h_ExitTimer) return GetLastError();

 if (!h_Mutex)     h_Mutex     = CreateMutex(0, FALSE, 0);
 if (!h_Mutex)     return GetLastError();

 return 0;
}

// -------------------------------------------------------------------------------------

TCP::cSocket::cLock::cLock()
{
 mh_Mutex = 0;
}

// Blocks a Request in SendTo() and Close()
// If cSocket is used single-threaded, Request() will never block
DWORD TCP::cSocket::cLock::Request(kLock* pk_Lock)
{
 DWORD u32_Error = pk_Lock->Init();
 if (u32_Error)
  return u32_Error;

 // FIRST: Block ProcessEvents() the next time BEFORE WaitForMultipleEvents() is reached
 if (!ResetEvent(pk_Lock->h_LoopEvent))
  return GetLastError();

 // SECOND: Escape from a blocking WaitForMultipleEvents()
 LARGE_INTEGER k_Interval;
 k_Interval.QuadPart = -1; // negative due time = set a relative time => fire timer in 100 nanoseconds

 #if TRACE_EVENTS || TRACE_LOCK
  cSocket::TraceA("Setting Timer: Lock Request Interval= 1 ns");
 #endif

 if (!SetWaitableTimer(pk_Lock->h_ExitTimer, &k_Interval, 0, 0, 0, FALSE))
  return GetLastError();

 #if TRACE_LOCK
  cSocket::TraceA("!!! cLock::Request WAIT");
 #endif
 
 // THIRD: Wait until ProcessEvents() has exited (only if multithreading)
 if (WaitForSingleObject(pk_Lock->h_Mutex, INFINITE) == WAIT_FAILED)
  return GetLastError();

 #if TRACE_LOCK
  cSocket::TraceA(">>> cLock::Request CONTINUE");
 #endif
 
 // Now we grabbed the Mutex -> allow ProcessEvents to continue
 if (!SetEvent(pk_Lock->h_LoopEvent))
  return GetLastError();

 // Store the mutex to be released in the destructor
 mh_Mutex = pk_Lock->h_Mutex;
 return 0;
}

// Blocks the endless loop ProcessEvents() after a Request() was made.
// If cSocket is used single-threaded, Loop() will never block
DWORD TCP::cSocket::cLock::Loop(kLock* pk_Lock)
{
 DWORD u32_Error = pk_Lock->Init();
 if (u32_Error)
  return u32_Error;

 #if TRACE_LOCK
  cSocket::TraceA("!!! cLock::Loop WAIT");
 #endif

 // The following line normally does not block (Event always set). Only after a Request() it
 // will block to give Request() a chance to grab the Mutex before the endless loop re-enters
 if (WaitForSingleObject(pk_Lock->h_LoopEvent, INFINITE) == WAIT_FAILED)
  return GetLastError();

 // Wait until SendTo() or Close() have exited
 if (WaitForSingleObject(pk_Lock->h_Mutex, INFINITE) == WAIT_FAILED)
  return GetLastError();

 #if TRACE_LOCK
  cSocket::TraceA(">>> cLock::Loop CONTINUE");
 #endif

 mh_Mutex = pk_Lock->h_Mutex;
 return 0;
}

// Destructor is executed, when the calling function (SendTo(), Close(), ProcessEvents()) has exited
TCP::cSocket::cLock::~cLock()
{
 #if TRACE_LOCK
  cSocket::TraceA("--- Release Mutex");
 #endif

 // Allow the other thread to continue its work
 ReleaseMutex(mh_Mutex);
}

/***************************************************************************************
****************************************************************************************

 embedded class cMemory
 
 Description:
 This tiny class allocates memory dynamically.
 The caller does not have to care about buffer sizes.
 This class is used to realize a FIFO memory for dynamic length datablocks.
 
 ATTENTION:
 With intention I do NOT use any functionalty of the STL library here.
 This would create a dependency to MSVCP70.DLL or MSVCP71.DLL or MSVCP80.DLL depending on the compiler.
 These DLLs are NOT available on every computer and must be installed with your application!
 
 Author:
 Elm?(www.netcult.ch/elmue)

****************************************************************************************
****************************************************************************************/

TCP::cSocket::cMemory::cMemory(DWORD u32_InitialSize)
{
  ms8_Mem  = new char[u32_InitialSize];
 mu32_Size = u32_InitialSize;
 mu32_Len  = 0;
}
TCP::cSocket::cMemory::~cMemory()
{
 delete ms8_Mem;
}
char* TCP::cSocket::cMemory::GetBuffer()
{
 return ms8_Mem;
}
DWORD TCP::cSocket::cMemory::GetLength()
{
 return mu32_Len;
}

// Append data to the end. If more memory is required, the current size is doubled.
void TCP::cSocket::cMemory::Append(char* s8_Data, DWORD u32_Count)
{
 DWORD u32_NewLen = mu32_Len + u32_Count;
 if (u32_NewLen > mu32_Size)
 {
  mu32_Size = max(u32_NewLen, mu32_Size *2);

  char*  s8_NewMem = new char[mu32_Size];
  memcpy(s8_NewMem, ms8_Mem, mu32_Len);
  
  delete ms8_Mem;
  ms8_Mem = s8_NewMem;
 }
 memcpy(ms8_Mem+mu32_Len, s8_Data, u32_Count);
 mu32_Len += u32_Count;
}

// Deletes the first u32_Count Bytes from the buffer by shifting down the Bytes that follow
void TCP::cSocket::cMemory::DeleteLeft(DWORD u32_Count)
{
 u32_Count = min(u32_Count, mu32_Len);
 memmove(ms8_Mem, ms8_Mem+u32_Count, mu32_Len-u32_Count);
 mu32_Len -= u32_Count;
}

/***************************************************************************************
****************************************************************************************

 embedded template class cHash
 
 Description:
 This tiny class allows to pass a list of unlimited size from a function to it's caller.
 The caller does not have to care to free the memory after using the data.
 This avoids memory leaks if the programmer forgets to call delete.
 
 ATTENTION:
 With intention I do NOT use any functionalty of the STL library here.
 This would create a dependency to MSVCP70.DLL or MSVCP71.DLL or MSVCP80.DLL depending on the compiler.
 These DLLs are NOT available on every computer and must be installed with your application!
 
 Author:
 Elm?(www.netcult.ch/elmue)

****************************************************************************************
****************************************************************************************/


//  Due to the way the C++ compiler works the template class must be defined in the header file

두서없이 붙여 넣기만 하고 설명은 안한다. 시간날때 하려고 한다. 그럼.

저작자 표시
신고


티스토리 툴바