Source files cleanup: OSSupport-related files in a separate subfolder, renamed.
git-svn-id: http://mc-server.googlecode.com/svn/trunk@885 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "BlockingTCPLink.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
#ifdef __MACH__
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBlockingTCPLink::cBlockingTCPLink(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cBlockingTCPLink::~cBlockingTCPLink()
|
||||
{
|
||||
CloseSocket();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockingTCPLink::CloseSocket()
|
||||
{
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
m_Socket.CloseSocket();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cBlockingTCPLink::Connect(const char * iAddress, unsigned int iPort)
|
||||
{
|
||||
ASSERT(!m_Socket.IsValid());
|
||||
if (m_Socket.IsValid())
|
||||
{
|
||||
LOGWARN("WARNING: cTCPLink Connect() called while still connected.");
|
||||
m_Socket.CloseSocket();
|
||||
}
|
||||
|
||||
struct hostent *hp;
|
||||
unsigned int addr;
|
||||
struct sockaddr_in server;
|
||||
|
||||
m_Socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
LOGERROR("cTCPLink: Cannot create a socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
addr = inet_addr(iAddress);
|
||||
hp = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
|
||||
if (hp == NULL)
|
||||
{
|
||||
//LOGWARN("cTCPLink: gethostbyaddr returned NULL");
|
||||
hp = gethostbyname(iAddress);
|
||||
if (hp == NULL)
|
||||
{
|
||||
LOGWARN("cTCPLink: Could not resolve %s", iAddress);
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
server.sin_addr.s_addr = *((unsigned long *)hp->h_addr);
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons( (unsigned short)iPort);
|
||||
if (connect(m_Socket, (struct sockaddr *)&server, sizeof(server)))
|
||||
{
|
||||
LOGWARN("cTCPLink: Connection to \"%s:%d\" failed (%s)", iAddress, iPort, cSocket::GetErrorString( cSocket::GetLastError() ).c_str() );
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBlockingTCPLink::Send(char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ )
|
||||
{
|
||||
ASSERT(m_Socket.IsValid());
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
LOGERROR("cBlockingTCPLink: Trying to send data without a valid connection!");
|
||||
return -1;
|
||||
}
|
||||
return m_Socket.Send(a_Data, a_Size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cBlockingTCPLink::SendMessage( const char* a_Message, int a_Flags /* = 0 */ )
|
||||
{
|
||||
ASSERT(m_Socket.IsValid());
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
LOGWARN("cBlockingTCPLink: Trying to send message without a valid connection!");
|
||||
return -1;
|
||||
}
|
||||
return m_Socket.Send(a_Message, strlen(a_Message));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockingTCPLink::ReceiveData(AString & oData)
|
||||
{
|
||||
ASSERT(m_Socket.IsValid());
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int Received = 0;
|
||||
char Buffer[256];
|
||||
while ((Received = recv(m_Socket, Buffer, sizeof(Buffer), 0)) > 0)
|
||||
{
|
||||
oData.append(Buffer, Received);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Socket.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cBlockingTCPLink //tolua_export
|
||||
{ //tolua_export
|
||||
public: //tolua_export
|
||||
cBlockingTCPLink(void); //tolua_export
|
||||
~cBlockingTCPLink(); //tolua_export
|
||||
|
||||
bool Connect( const char* a_Address, unsigned int a_Port ); //tolua_export
|
||||
int Send( char* a_Data, unsigned int a_Size, int a_Flags = 0 ); //tolua_export
|
||||
int SendMessage( const char* a_Message, int a_Flags = 0 ); //tolua_export
|
||||
void CloseSocket(); //tolua_export
|
||||
void ReceiveData(AString & oData); //tolua_export
|
||||
protected:
|
||||
|
||||
cSocket m_Socket;
|
||||
}; //tolua_export
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
#include "IsThread.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCriticalSection:
|
||||
|
||||
cCriticalSection::cCriticalSection()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
InitializeCriticalSection( &m_CriticalSection );
|
||||
#else
|
||||
m_Attributes = new pthread_mutexattr_t;
|
||||
pthread_mutexattr_init((pthread_mutexattr_t*)m_Attributes);
|
||||
pthread_mutexattr_settype((pthread_mutexattr_t*)m_Attributes, PTHREAD_MUTEX_RECURSIVE);
|
||||
|
||||
m_CriticalSectionPtr = new pthread_mutex_t;
|
||||
if( pthread_mutex_init( (pthread_mutex_t*)m_CriticalSectionPtr, (pthread_mutexattr_t*)m_Attributes ) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not initialize Critical Section!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCriticalSection::~cCriticalSection()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DeleteCriticalSection( &m_CriticalSection );
|
||||
#else
|
||||
if( pthread_mutex_destroy( (pthread_mutex_t*)m_CriticalSectionPtr ) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not destroy Critical Section!");
|
||||
}
|
||||
delete (pthread_mutex_t*)m_CriticalSectionPtr;
|
||||
pthread_mutexattr_destroy( (pthread_mutexattr_t*)m_Attributes );
|
||||
delete (pthread_mutexattr_t*)m_Attributes;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCriticalSection::Lock()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
EnterCriticalSection( &m_CriticalSection );
|
||||
#else
|
||||
pthread_mutex_lock( (pthread_mutex_t*)m_CriticalSectionPtr );
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
m_IsLocked = true;
|
||||
m_OwningThreadID = cIsThread::GetCurrentID();
|
||||
#endif // _DEBUG
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCriticalSection::Unlock()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
m_IsLocked = false;
|
||||
#endif // _DEBUG
|
||||
|
||||
#ifdef _WIN32
|
||||
LeaveCriticalSection( &m_CriticalSection );
|
||||
#else
|
||||
pthread_mutex_unlock( (pthread_mutex_t*)m_CriticalSectionPtr );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
bool cCriticalSection::IsLocked(void)
|
||||
{
|
||||
return m_IsLocked;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cCriticalSection::IsLockedByCurrentThread(void)
|
||||
{
|
||||
return m_IsLocked && (m_OwningThreadID == cIsThread::GetCurrentID());
|
||||
}
|
||||
#endif // _DEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCSLock
|
||||
|
||||
cCSLock::cCSLock(cCriticalSection * a_CS)
|
||||
: m_CS(a_CS)
|
||||
, m_IsLocked(false)
|
||||
{
|
||||
Lock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCSLock::cCSLock(cCriticalSection & a_CS)
|
||||
: m_CS(&a_CS)
|
||||
, m_IsLocked(false)
|
||||
{
|
||||
Lock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCSLock::~cCSLock()
|
||||
{
|
||||
if (!m_IsLocked)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCSLock::Lock(void)
|
||||
{
|
||||
ASSERT(!m_IsLocked);
|
||||
m_IsLocked = true;
|
||||
m_CS->Lock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cCSLock::Unlock(void)
|
||||
{
|
||||
ASSERT(m_IsLocked);
|
||||
m_IsLocked = false;
|
||||
m_CS->Unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cCSUnlock:
|
||||
|
||||
cCSUnlock::cCSUnlock(cCSLock & a_Lock) :
|
||||
m_Lock(a_Lock)
|
||||
{
|
||||
m_Lock.Unlock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cCSUnlock::~cCSUnlock()
|
||||
{
|
||||
m_Lock.Lock();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cCriticalSection
|
||||
{
|
||||
public:
|
||||
cCriticalSection(void);
|
||||
~cCriticalSection();
|
||||
|
||||
void Lock(void);
|
||||
void Unlock(void);
|
||||
|
||||
#ifdef _DEBUG
|
||||
bool IsLocked(void);
|
||||
bool IsLockedByCurrentThread(void);
|
||||
#endif // _DEBUG
|
||||
|
||||
private:
|
||||
#ifdef _DEBUG
|
||||
bool m_IsLocked;
|
||||
unsigned long m_OwningThreadID;
|
||||
#endif // _DEBUG
|
||||
|
||||
#ifdef _WIN32
|
||||
CRITICAL_SECTION m_CriticalSection;
|
||||
#else // _WIN32
|
||||
void* m_CriticalSectionPtr ALIGN_8; // Pointer to a CRITICAL_SECTION object
|
||||
void* m_Attributes ALIGN_8;
|
||||
#endif // else _WIN32
|
||||
} ALIGN_8;
|
||||
|
||||
|
||||
|
||||
|
||||
/// RAII for cCriticalSection - locks the CS on creation, unlocks on destruction
|
||||
class cCSLock
|
||||
{
|
||||
cCriticalSection * m_CS;
|
||||
|
||||
// Unlike a cCriticalSection, this object should be used from a single thread, therefore access to m_IsLocked is not threadsafe
|
||||
// In Windows, it is an error to call cCriticalSection::Unlock() multiple times if the lock is not held,
|
||||
// therefore we need to check this value whether we are locked or not.
|
||||
bool m_IsLocked;
|
||||
|
||||
public:
|
||||
cCSLock(cCriticalSection * a_CS);
|
||||
cCSLock(cCriticalSection & a_CS);
|
||||
~cCSLock();
|
||||
|
||||
// Temporarily unlock or re-lock:
|
||||
void Lock(void);
|
||||
void Unlock(void);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(cCSLock);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Temporary RAII unlock for a cCSLock. Useful for unlock-wait-relock scenarios
|
||||
class cCSUnlock
|
||||
{
|
||||
cCSLock & m_Lock;
|
||||
public:
|
||||
cCSUnlock(cCSLock & a_Lock);
|
||||
~cCSUnlock();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(cCSUnlock);
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
|
||||
// Event.cpp
|
||||
|
||||
// Implements the cEvent object representing an OS-specific synchronization primitive that can be waited-for
|
||||
// Implemented as an Event on Win and as a 1-semaphore on *nix
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Event.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cEvent::cEvent(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_Event = CreateEvent( 0, FALSE, FALSE, 0 );
|
||||
if (m_Event == NULL)
|
||||
{
|
||||
LOGERROR("cEvent: cannot create event, GLE = %d. Aborting server.", GetLastError());
|
||||
abort();
|
||||
}
|
||||
#else // *nix
|
||||
m_bIsNamed = false;
|
||||
m_Event = new sem_t;
|
||||
if (sem_init(m_Event, 0, 0))
|
||||
{
|
||||
// This path is used by MacOS, because it doesn't support unnamed semaphores.
|
||||
delete m_Event;
|
||||
m_bIsNamed = true;
|
||||
|
||||
AString EventName;
|
||||
Printf(EventName, "cEvent%p", this);
|
||||
m_Event = sem_open(EventName.c_str(), O_CREAT, 777, 0 );
|
||||
if (m_Event == SEM_FAILED)
|
||||
{
|
||||
LOGERROR("cEvent: Cannot create event, errno = %i. Aborting server.", errno);
|
||||
abort();
|
||||
}
|
||||
// Unlink the semaphore immediately - it will continue to function but will not pollute the namespace
|
||||
// We don't store the name, so can't call this in the destructor
|
||||
if (sem_unlink(EventName.c_str()) != 0)
|
||||
{
|
||||
LOGWARN("ERROR: Could not unlink cEvent. (%i)", errno);
|
||||
}
|
||||
}
|
||||
#endif // *nix
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cEvent::~cEvent()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CloseHandle(m_Event);
|
||||
#else
|
||||
if (m_bIsNamed)
|
||||
{
|
||||
if (sem_close(m_Event) != 0)
|
||||
{
|
||||
LOGERROR("ERROR: Could not close cEvent. (%i)", errno);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sem_destroy(m_Event);
|
||||
delete m_Event;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEvent::Wait(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD res = WaitForSingleObject(m_Event, INFINITE);
|
||||
if (res != WAIT_OBJECT_0)
|
||||
{
|
||||
LOGWARN("cEvent: waiting for the event failed: %d, GLE = %d. Continuing, but server may be unstable.", res, GetLastError());
|
||||
}
|
||||
#else
|
||||
int res = sem_wait(m_Event);
|
||||
if (res != 0 )
|
||||
{
|
||||
LOGWARN("cEvent: waiting for the event failed: %i, errno = %i. Continuing, but server may be unstable.", res, errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cEvent::Set(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!SetEvent(m_Event))
|
||||
{
|
||||
LOGWARN("cEvent: Could not set cEvent: GLE = %d", GetLastError());
|
||||
}
|
||||
#else
|
||||
int res = sem_post(m_Event);
|
||||
if (res != 0)
|
||||
{
|
||||
LOGWARN("cEvent: Could not set cEvent: %i, errno = %d", res, errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
// Event.h
|
||||
|
||||
// Interfaces to the cEvent object representing an OS-specific synchronization primitive that can be waited-for
|
||||
// Implemented as an Event on Win and as a 1-semaphore on *nix
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef CEVENT_H_INCLUDED
|
||||
#define CEVENT_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cEvent
|
||||
{
|
||||
public:
|
||||
cEvent(void);
|
||||
~cEvent();
|
||||
|
||||
void Wait(void);
|
||||
void Set (void);
|
||||
|
||||
private:
|
||||
|
||||
#ifdef _WIN32
|
||||
HANDLE m_Event;
|
||||
#else
|
||||
sem_t * m_Event;
|
||||
bool m_bIsNamed;
|
||||
#endif
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // CEVENT_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,271 @@
|
||||
|
||||
// cFile.cpp
|
||||
|
||||
// Implements the cFile class providing an OS-independent abstraction of a file.
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "File.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Simple constructor - creates an unopened file object, use Open() to open / create a real file
|
||||
cFile::cFile(void) :
|
||||
#ifdef USE_STDIO_FILE
|
||||
m_File(NULL)
|
||||
#else
|
||||
m_File(INVALID_HANDLE_VALUE)
|
||||
#endif // USE_STDIO_FILE
|
||||
{
|
||||
// Nothing needed yet
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Constructs and opens / creates the file specified, use IsOpen() to check for success
|
||||
cFile::cFile(const AString & iFileName, EMode iMode) :
|
||||
#ifdef USE_STDIO_FILE
|
||||
m_File(NULL)
|
||||
#else
|
||||
m_File(INVALID_HANDLE_VALUE)
|
||||
#endif // USE_STDIO_FILE
|
||||
{
|
||||
Open(iFileName, iMode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Auto-closes the file, if open
|
||||
cFile::~cFile()
|
||||
{
|
||||
if (IsOpen())
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFile::Open(const AString & iFileName, EMode iMode)
|
||||
{
|
||||
ASSERT(!IsOpen()); // You should close the file before opening another one
|
||||
|
||||
if (IsOpen())
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
const char * Mode = NULL;
|
||||
switch (iMode)
|
||||
{
|
||||
case fmRead: Mode = "rb"; break;
|
||||
case fmWrite: Mode = "wb"; break;
|
||||
case fmReadWrite: Mode = "rb+"; break;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled file mode");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), Mode);
|
||||
if ((m_File == NULL) && (iMode == fmReadWrite))
|
||||
{
|
||||
// Fix for MS not following C spec, opening "a" mode files for writing at the end only
|
||||
// The file open operation has been tried with "read update", fails if file not found
|
||||
// So now we know either the file doesn't exist or we don't have rights, no need to worry about file contents.
|
||||
// Simply re-open for read-writing, erasing existing contents:
|
||||
m_File = fopen( (FILE_IO_PREFIX + iFileName).c_str(), "wb+");
|
||||
}
|
||||
return (m_File != NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cFile::Close(void)
|
||||
{
|
||||
ASSERT(IsOpen()); // You should not close file objects that don't have an open file.
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(m_File);
|
||||
m_File = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFile::IsOpen(void) const
|
||||
{
|
||||
return (m_File != NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFile::IsEOF(void) const
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
// Unopened files behave as at EOF
|
||||
return true;
|
||||
}
|
||||
|
||||
return (feof(m_File) != 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open
|
||||
int cFile::Read (void * iBuffer, int iNumBytes)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fread(iBuffer, 1, iNumBytes, m_File); // fread() returns the portion of Count parameter actually read, so we need to send iNumBytes as Count
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open
|
||||
int cFile::Write(const void * iBuffer, int iNumBytes)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int res = fwrite(iBuffer, 1, iNumBytes, m_File); // fwrite() returns the portion of Count parameter actually written, so we need to send iNumBytes as Count
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Seeks to iPosition bytes from file start, returns old position or -1 for failure
|
||||
int cFile::Seek (int iPosition)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(m_File, iPosition, SEEK_SET) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return ftell(m_File);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Returns the current position (bytes from file start)
|
||||
int cFile::Tell (void) const
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ftell(m_File);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Returns the size of file, in bytes, or -1 for failure; asserts if not open
|
||||
int cFile::GetSize(void) const
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int CurPos = ftell(m_File);
|
||||
if (CurPos < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
if (fseek(m_File, 0, SEEK_END) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
int res = ftell(m_File);
|
||||
if (fseek(m_File, CurPos, SEEK_SET) != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cFile::ReadRestOfFile(AString & a_Contents)
|
||||
{
|
||||
ASSERT(IsOpen());
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int DataSize = GetSize() - Tell();
|
||||
|
||||
// HACK: This depends on the internal knowledge that AString's data() function returns the internal buffer directly
|
||||
a_Contents.assign(DataSize, '\0');
|
||||
return Read((void *)a_Contents.data(), DataSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cFile::Exists(const AString & a_FileName)
|
||||
{
|
||||
cFile test(a_FileName, fmRead);
|
||||
return test.IsOpen();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
// cFile.h
|
||||
|
||||
// Interfaces to the cFile class providing an OS-independent abstraction of a file.
|
||||
|
||||
/*
|
||||
The object is optimized towards binary reads.
|
||||
The object has no multithreading locks, don't use from multiple threads!
|
||||
Usage:
|
||||
1, Construct a cFile instance (no-param constructor)
|
||||
2, Open a file using Open(), check return value for success
|
||||
3, Read / write
|
||||
4, Destroy the instance
|
||||
|
||||
-- OR --
|
||||
|
||||
1, Construct a cFile instance opening the file (filename-param constructor)
|
||||
2, Check if the file was opened using IsOpen()
|
||||
3, Read / write
|
||||
4, Destroy the instance
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef CFILE_H_INCLUDED
|
||||
#define CFILE_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
#define USE_STDIO_FILE
|
||||
#endif // _WIN32
|
||||
|
||||
// DEBUG:
|
||||
#define USE_STDIO_FILE
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cFile
|
||||
{
|
||||
public:
|
||||
/// The mode in which to open the file
|
||||
enum EMode
|
||||
{
|
||||
fmRead, // Read-only. If the file doesn't exist, object will not be valid
|
||||
fmWrite, // Write-only. If the file already exists, it will be overwritten
|
||||
fmReadWrite // Read/write. If the file already exists, it will be left intact; writing will overwrite the data from the beginning
|
||||
} ;
|
||||
|
||||
/// Simple constructor - creates an unopened file object, use Open() to open / create a real file
|
||||
cFile(void);
|
||||
|
||||
/// Constructs and opens / creates the file specified, use IsOpen() to check for success
|
||||
cFile(const AString & iFileName, EMode iMode);
|
||||
|
||||
/// Auto-closes the file, if open
|
||||
~cFile();
|
||||
|
||||
bool Open(const AString & iFileName, EMode iMode);
|
||||
void Close(void);
|
||||
bool IsOpen(void) const;
|
||||
bool IsEOF(void) const;
|
||||
|
||||
/// Reads up to iNumBytes bytes into iBuffer, returns the number of bytes actually read, or -1 on failure; asserts if not open
|
||||
int Read (void * iBuffer, int iNumBytes);
|
||||
|
||||
/// Writes up to iNumBytes bytes from iBuffer, returns the number of bytes actually written, or -1 on failure; asserts if not open
|
||||
int Write(const void * iBuffer, int iNumBytes);
|
||||
|
||||
/// Seeks to iPosition bytes from file start, returns old position or -1 for failure; asserts if not open
|
||||
int Seek (int iPosition);
|
||||
|
||||
/// Returns the current position (bytes from file start) or -1 for failure; asserts if not open
|
||||
int Tell (void) const;
|
||||
|
||||
/// Returns the size of file, in bytes, or -1 for failure; asserts if not open
|
||||
int GetSize(void) const;
|
||||
|
||||
/// Reads the file from current position till EOF into an AString; returns the number of bytes read or -1 for error
|
||||
int ReadRestOfFile(AString & a_Contents);
|
||||
|
||||
/// Returns true if the file specified exists
|
||||
static bool Exists(const AString & a_FileName);
|
||||
|
||||
private:
|
||||
#ifdef USE_STDIO_FILE
|
||||
FILE * m_File;
|
||||
#else
|
||||
HANDLE m_File;
|
||||
#endif
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // CFILE_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
|
||||
// IsThread.cpp
|
||||
|
||||
// Implements the cIsThread class representing an OS-independent wrapper for a class that implements a thread.
|
||||
// This class will eventually suupersede the old cThread class
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "IsThread.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
|
||||
#if defined(_MSC_VER) && defined(_DEBUG)
|
||||
//
|
||||
// Usage: SetThreadName (-1, "MainThread");
|
||||
//
|
||||
|
||||
static void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
|
||||
{
|
||||
struct
|
||||
{
|
||||
DWORD dwType; // must be 0x1000
|
||||
LPCSTR szName; // pointer to name (in user addr space)
|
||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||
DWORD dwFlags; // reserved for future use, must be zero
|
||||
} info;
|
||||
|
||||
info.dwType = 0x1000;
|
||||
info.szName = szThreadName;
|
||||
info.dwThreadID = dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (DWORD *)&info);
|
||||
}
|
||||
__except(EXCEPTION_CONTINUE_EXECUTION)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif // _MSC_VER && _DEBUG
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cIsThread:
|
||||
|
||||
cIsThread::cIsThread(const AString & iThreadName) :
|
||||
m_ThreadName(iThreadName),
|
||||
m_ShouldTerminate(false),
|
||||
#ifdef _WIN32
|
||||
m_Handle(NULL)
|
||||
#else // _WIN32
|
||||
m_HasStarted(false)
|
||||
#endif // else _WIN32
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cIsThread::~cIsThread()
|
||||
{
|
||||
m_ShouldTerminate = true;
|
||||
Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cIsThread::Start(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
ASSERT(m_Handle == NULL); // Has already started one thread?
|
||||
|
||||
// Create the thread suspended, so that the mHandle variable is valid in the thread procedure
|
||||
DWORD ThreadID = 0;
|
||||
m_Handle = CreateThread(NULL, 0, thrExecute, this, CREATE_SUSPENDED, &ThreadID);
|
||||
if (m_Handle == NULL)
|
||||
{
|
||||
LOGERROR("ERROR: Could not create thread \"%s\", GLE = %d!", m_ThreadName.c_str(), GetLastError());
|
||||
return false;
|
||||
}
|
||||
ResumeThread(m_Handle);
|
||||
|
||||
#if defined(_DEBUG) && defined(_MSC_VER)
|
||||
// Thread naming is available only in MSVC
|
||||
if (!m_ThreadName.empty())
|
||||
{
|
||||
SetThreadName(ThreadID, m_ThreadName.c_str());
|
||||
}
|
||||
#endif // _DEBUG and _MSC_VER
|
||||
|
||||
#else // _WIN32
|
||||
ASSERT(!m_HasStarted);
|
||||
|
||||
if (pthread_create(&m_Handle, NULL, thrExecute, this))
|
||||
{
|
||||
LOGERROR("ERROR: Could not create thread \"%s\", !", m_ThreadName.c_str());
|
||||
return false;
|
||||
}
|
||||
m_HasStarted = true;
|
||||
#endif // else _WIN32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cIsThread::Wait(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
if (m_Handle == NULL)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Cannot log, logger may already be stopped:
|
||||
// LOG("Waiting for thread \"%s\" to terminate.", m_ThreadName.c_str());
|
||||
int res = WaitForSingleObject(m_Handle, INFINITE);
|
||||
m_Handle = NULL;
|
||||
// Cannot log, logger may already be stopped:
|
||||
// LOG("Thread \"%s\" %s terminated, GLE = %d", m_ThreadName.c_str(), (res == WAIT_OBJECT_0) ? "" : "not", GetLastError());
|
||||
return (res == WAIT_OBJECT_0);
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
if (!m_HasStarted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Cannot log, logger may already be stopped:
|
||||
// LOG("Waiting for thread \"%s\" to terminate.", m_ThreadName.c_str());
|
||||
int res = pthread_join(m_Handle, NULL);
|
||||
m_HasStarted = false;
|
||||
// Cannot log, logger may already be stopped:
|
||||
// LOG("Thread \"%s\" %s terminated, errno = %d", m_ThreadName.c_str(), (res == 0) ? "" : "not", errno);
|
||||
return (res == 0);
|
||||
|
||||
#endif // else _WIN32
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned long cIsThread::GetCurrentID(void)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (unsigned long) GetCurrentThreadId();
|
||||
#else
|
||||
return (unsigned long) pthread_self();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
|
||||
// IsThread.h
|
||||
|
||||
// Interfaces to the cIsThread class representing an OS-independent wrapper for a class that implements a thread.
|
||||
// This class will eventually suupersede the old cThread class
|
||||
|
||||
/*
|
||||
Usage:
|
||||
To have a new thread, declare a class descending from cIsClass.
|
||||
Then override its Execute() method to provide your thread processing.
|
||||
In the descending class' constructor call the Start() method to start the thread once you're finished with initialization.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef CISTHREAD_H_INCLUDED
|
||||
#define CISTHREAD_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cIsThread
|
||||
{
|
||||
protected:
|
||||
virtual void Execute(void) = 0; // This function is called in the new thread's context
|
||||
|
||||
volatile bool m_ShouldTerminate; // The overriden Execute() method should check this periodically and terminate if this is true
|
||||
|
||||
public:
|
||||
cIsThread(const AString & iThreadName);
|
||||
~cIsThread();
|
||||
|
||||
bool Start(void); // Starts the thread
|
||||
bool Wait(void); // Waits for the thread to finish
|
||||
|
||||
static unsigned long GetCurrentID(void); // Returns the OS-dependent thread ID for the caller's thread
|
||||
|
||||
private:
|
||||
AString m_ThreadName;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
HANDLE m_Handle;
|
||||
|
||||
static DWORD_PTR __stdcall thrExecute(LPVOID a_Param)
|
||||
{
|
||||
((cIsThread *)a_Param)->Execute();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
pthread_t m_Handle;
|
||||
bool m_HasStarted;
|
||||
|
||||
static void * thrExecute(void * a_Param)
|
||||
{
|
||||
((cIsThread *)a_Param)->Execute();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // else _WIN32
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // CISTHREAD_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "MakeDir.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMakeDir::MakeDir(const AString & a_Directory)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
SECURITY_ATTRIBUTES Attrib;
|
||||
Attrib.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
Attrib.lpSecurityDescriptor = NULL;
|
||||
Attrib.bInheritHandle = false;
|
||||
::CreateDirectory( (FILE_IO_PREFIX + a_Directory).c_str(), &Attrib);
|
||||
#else
|
||||
mkdir( (FILE_IO_PREFIX + a_Directory).c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cMakeDir
|
||||
{
|
||||
public:
|
||||
static void MakeDir(const AString & a_Directory);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSemaphore::cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount /* = 0 */ )
|
||||
#ifndef _WIN32
|
||||
: m_bNamed( false )
|
||||
#endif
|
||||
{
|
||||
#ifndef _WIN32
|
||||
(void)a_MaxCount;
|
||||
m_Handle = new sem_t;
|
||||
if (sem_init( (sem_t*)m_Handle, 0, 0))
|
||||
{
|
||||
LOG("WARNING cSemaphore: Could not create unnamed semaphore, fallback to named.");
|
||||
delete (sem_t*)m_Handle; // named semaphores return their own address
|
||||
m_bNamed = true;
|
||||
|
||||
AString Name;
|
||||
Printf(Name, "cSemaphore%p", this );
|
||||
m_Handle = sem_open(Name.c_str(), O_CREAT, 777, a_InitialCount);
|
||||
if( m_Handle == SEM_FAILED )
|
||||
{
|
||||
LOG("ERROR: Could not create Semaphore. (%i)", errno );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( sem_unlink(Name.c_str()) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not unlink cSemaphore. (%i)", errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
m_Handle = CreateSemaphore(
|
||||
NULL, // security attribute
|
||||
a_InitialCount, // initial count
|
||||
a_MaxCount, // maximum count
|
||||
0 // name (optional)
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
cSemaphore::~cSemaphore()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
CloseHandle( m_Handle );
|
||||
#else
|
||||
if( m_bNamed )
|
||||
{
|
||||
if( sem_close( (sem_t*)m_Handle ) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not close cSemaphore. (%i)", errno);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sem_destroy( (sem_t*)m_Handle );
|
||||
delete (sem_t*)m_Handle;
|
||||
}
|
||||
m_Handle = 0;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void cSemaphore::Wait()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if( sem_wait( (sem_t*)m_Handle ) != 0)
|
||||
{
|
||||
LOG("ERROR: Could not wait for cSemaphore. (%i)", errno);
|
||||
}
|
||||
#else
|
||||
WaitForSingleObject( m_Handle, INFINITE);
|
||||
#endif
|
||||
}
|
||||
|
||||
void cSemaphore::Signal()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if( sem_post( (sem_t*)m_Handle ) != 0 )
|
||||
{
|
||||
LOG("ERROR: Could not signal cSemaphore. (%i)", errno);
|
||||
}
|
||||
#else
|
||||
ReleaseSemaphore( m_Handle, 1, NULL );
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
class cSemaphore
|
||||
{
|
||||
public:
|
||||
cSemaphore( unsigned int a_MaxCount, unsigned int a_InitialCount = 0 );
|
||||
~cSemaphore();
|
||||
|
||||
void Wait();
|
||||
void Signal();
|
||||
private:
|
||||
void* m_Handle; // HANDLE pointer
|
||||
|
||||
#ifndef _WIN32
|
||||
bool m_bNamed;
|
||||
#endif
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSleep::MilliSleep( unsigned int a_MilliSeconds )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
Sleep(a_MilliSeconds); // Don't tick too much
|
||||
#else
|
||||
usleep(a_MilliSeconds*1000);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
class cSleep
|
||||
{
|
||||
public:
|
||||
static void MilliSleep( unsigned int a_MilliSeconds );
|
||||
};
|
||||
@@ -0,0 +1,331 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Socket.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h> //inet_ntoa()
|
||||
#else
|
||||
#define socklen_t int
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocket::cSocket(xSocket a_Socket)
|
||||
: m_Socket(a_Socket)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocket::~cSocket()
|
||||
{
|
||||
// Do NOT close the socket; this class is an API wrapper, not a RAII!
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocket::operator cSocket::xSocket() const
|
||||
{
|
||||
return m_Socket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocket::xSocket cSocket::GetSocket() const
|
||||
{
|
||||
return m_Socket;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocket::IsValid(void) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (m_Socket != INVALID_SOCKET);
|
||||
#else // _WIN32
|
||||
return (m_Socket >= 0);
|
||||
#endif // else _WIN32
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocket::CloseSocket()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
closesocket(m_Socket);
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
if (shutdown(m_Socket, SHUT_RDWR) != 0)//SD_BOTH);
|
||||
{
|
||||
LOGWARN("Error on shutting down socket (%s): %s", m_IPString.c_str(), GetLastErrorString().c_str());
|
||||
}
|
||||
if (close(m_Socket) != 0)
|
||||
{
|
||||
LOGWARN("Error closing socket (%s): %s", m_IPString.c_str(), GetLastErrorString().c_str());
|
||||
}
|
||||
|
||||
#endif // else _WIN32
|
||||
|
||||
// Invalidate the socket so that this object can be re-used for another connection
|
||||
m_Socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cSocket::GetErrorString( int a_ErrNo )
|
||||
{
|
||||
char buffer[ 1024 ];
|
||||
AString Out;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, a_ErrNo, 0, buffer, ARRAYCOUNT(buffer), NULL);
|
||||
Printf(Out, "%d: %s", a_ErrNo, buffer);
|
||||
if (!Out.empty() && (Out[Out.length() - 1] == '\n'))
|
||||
{
|
||||
Out.erase(Out.length() - 2);
|
||||
}
|
||||
return Out;
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
// According to http://linux.die.net/man/3/strerror_r there are two versions of strerror_r():
|
||||
|
||||
#if ( _GNU_SOURCE ) && !defined(ANDROID_NDK) // GNU version of strerror_r()
|
||||
|
||||
char * res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
|
||||
if( res != NULL )
|
||||
{
|
||||
Printf(Out, "%d: %s", a_ErrNo, res);
|
||||
return Out;
|
||||
}
|
||||
|
||||
#else // XSI version of strerror_r():
|
||||
|
||||
int res = strerror_r( errno, buffer, ARRAYCOUNT(buffer) );
|
||||
if( res == 0 )
|
||||
{
|
||||
Printf(Out, "%d: %s", a_ErrNo, buffer);
|
||||
return Out;
|
||||
}
|
||||
|
||||
#endif // strerror_r() version
|
||||
|
||||
else
|
||||
{
|
||||
Printf(Out, "Error %d while getting error string for error #%d!", errno, a_ErrNo);
|
||||
return Out;
|
||||
}
|
||||
|
||||
#endif // else _WIN32
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::GetLastError()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return WSAGetLastError();
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::SetReuseAddress()
|
||||
{
|
||||
#if defined(_WIN32) || defined(ANDROID_NDK)
|
||||
char yes = 1;
|
||||
#else
|
||||
int yes = 1;
|
||||
#endif
|
||||
return setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::WSAStartup()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
memset(&wsaData, 0, sizeof(wsaData));
|
||||
return ::WSAStartup(MAKEWORD(2, 2),&wsaData);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocket cSocket::CreateSocket()
|
||||
{
|
||||
return socket(AF_INET,SOCK_STREAM,0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned long cSocket::INTERNET_ADDRESS_LOCALHOST(void)
|
||||
{
|
||||
static unsigned long LocalHost = 0;
|
||||
if (LocalHost == 0)
|
||||
{
|
||||
LocalHost = inet_addr("127.0.0.1"); // GCC won't accept this as a global var assignment
|
||||
}
|
||||
return LocalHost;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::Bind(SockAddr_In& a_Address)
|
||||
{
|
||||
sockaddr_in local;
|
||||
memset(&local, 0, sizeof(local));
|
||||
|
||||
local.sin_family = a_Address.Family;
|
||||
local.sin_addr.s_addr = a_Address.Address;
|
||||
local.sin_port = htons((u_short)a_Address.Port);
|
||||
|
||||
return bind(m_Socket, (sockaddr*)&local, sizeof(local));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::Listen(int a_Backlog)
|
||||
{
|
||||
return listen(m_Socket, a_Backlog);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocket cSocket::Accept()
|
||||
{
|
||||
sockaddr_in from;
|
||||
socklen_t fromlen=sizeof(from);
|
||||
|
||||
cSocket SClient = accept(m_Socket, (sockaddr*)&from, &fromlen);
|
||||
|
||||
if (from.sin_addr.s_addr && SClient.IsValid()) // Get IP in string form
|
||||
{
|
||||
SClient.m_IPString = inet_ntoa(from.sin_addr);
|
||||
//LOG("cSocket::Accept() %s", SClient.m_IPString);
|
||||
}
|
||||
|
||||
return SClient;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::Connect(SockAddr_In & a_Address)
|
||||
{
|
||||
sockaddr_in local;
|
||||
|
||||
local.sin_family = a_Address.Family;
|
||||
local.sin_addr.s_addr = a_Address.Address;
|
||||
local.sin_port = htons((u_short)a_Address.Port);
|
||||
|
||||
return connect(m_Socket, (sockaddr *)&local, sizeof(local));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::Connect(const AString & a_HostNameOrAddr, unsigned short a_Port)
|
||||
{
|
||||
// First try IP Address string to hostent conversion, because it's faster
|
||||
unsigned long addr = inet_addr(a_HostNameOrAddr.c_str());
|
||||
hostent * hp = gethostbyaddr((char*)&addr, sizeof(addr), AF_INET);
|
||||
if (hp == NULL)
|
||||
{
|
||||
// It is not an IP Address string, but rather a regular hostname, resolve:
|
||||
hp = gethostbyname(a_HostNameOrAddr.c_str());
|
||||
if (hp == NULL)
|
||||
{
|
||||
LOGWARN("cTCPLink: Could not resolve hostname \"%s\"", a_HostNameOrAddr.c_str());
|
||||
CloseSocket();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
sockaddr_in server;
|
||||
server.sin_addr.s_addr = *((unsigned long*)hp->h_addr);
|
||||
server.sin_family = AF_INET;
|
||||
server.sin_port = htons( (unsigned short)a_Port );
|
||||
return connect(m_Socket, (sockaddr *)&server, sizeof(server));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::Receive(char* a_Buffer, unsigned int a_Length, unsigned int a_Flags)
|
||||
{
|
||||
return recv(m_Socket, a_Buffer, a_Length, a_Flags);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cSocket::Send(const char * a_Buffer, unsigned int a_Length)
|
||||
{
|
||||
return send(m_Socket, a_Buffer, a_Length, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
unsigned short cSocket::GetPort(void) const
|
||||
{
|
||||
ASSERT(IsValid());
|
||||
|
||||
sockaddr_in Addr;
|
||||
socklen_t AddrSize = sizeof(Addr);
|
||||
if (getsockname(m_Socket, (sockaddr *)&Addr, &AddrSize) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ntohs(Addr.sin_port);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cSocket
|
||||
{
|
||||
public:
|
||||
#ifdef _WIN32
|
||||
typedef SOCKET xSocket;
|
||||
#else
|
||||
typedef int xSocket;
|
||||
static const int INVALID_SOCKET = -1;
|
||||
#endif
|
||||
|
||||
cSocket(void) : m_Socket(INVALID_SOCKET) {}
|
||||
cSocket(xSocket a_Socket);
|
||||
~cSocket();
|
||||
|
||||
bool IsValid(void) const;
|
||||
void CloseSocket();
|
||||
|
||||
operator xSocket() const;
|
||||
xSocket GetSocket() const;
|
||||
|
||||
bool operator == (const cSocket & a_Other) {return m_Socket == a_Other.m_Socket; }
|
||||
|
||||
void SetSocket( xSocket a_Socket );
|
||||
|
||||
int SetReuseAddress();
|
||||
static int WSAStartup();
|
||||
|
||||
static AString GetErrorString( int a_ErrNo );
|
||||
static int GetLastError();
|
||||
static AString GetLastErrorString(void)
|
||||
{
|
||||
return GetErrorString(GetLastError());
|
||||
}
|
||||
|
||||
static cSocket CreateSocket();
|
||||
|
||||
inline static bool IsSocketError( int a_ReturnedValue )
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (a_ReturnedValue == SOCKET_ERROR || a_ReturnedValue == 0);
|
||||
#else
|
||||
return (a_ReturnedValue <= 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct SockAddr_In
|
||||
{
|
||||
short Family;
|
||||
unsigned short Port;
|
||||
unsigned long Address;
|
||||
};
|
||||
|
||||
static const short ADDRESS_FAMILY_INTERNET = 2;
|
||||
static const unsigned long INTERNET_ADDRESS_ANY = 0;
|
||||
static unsigned long INTERNET_ADDRESS_LOCALHOST(void); // 127.0.0.1 represented in network byteorder; must be a function due to GCC :(
|
||||
|
||||
int Bind( SockAddr_In& a_Address );
|
||||
int Listen( int a_Backlog );
|
||||
cSocket Accept();
|
||||
int Connect(SockAddr_In & a_Address); // Returns 0 on success, !0 on failure
|
||||
int Connect(const AString & a_HostNameOrAddr, unsigned short a_Port); // Returns 0 on success, !0 on failure
|
||||
int Receive( char* a_Buffer, unsigned int a_Length, unsigned int a_Flags );
|
||||
int Send (const char * a_Buffer, unsigned int a_Length);
|
||||
|
||||
unsigned short GetPort(void) const; // Returns 0 on failure
|
||||
|
||||
const AString & GetIPString(void) const { return m_IPString; }
|
||||
|
||||
private:
|
||||
xSocket m_Socket;
|
||||
AString m_IPString;
|
||||
};
|
||||
@@ -0,0 +1,713 @@
|
||||
|
||||
// cSocketThreads.cpp
|
||||
|
||||
// Implements the cSocketThreads class representing the heart of MCS's client networking.
|
||||
// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support
|
||||
// For more detail, see http://forum.mc-server.org/showthread.php?tid=327
|
||||
|
||||
#include "Globals.h"
|
||||
#include "SocketThreads.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// cSocketThreads:
|
||||
|
||||
cSocketThreads::cSocketThreads(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocketThreads::~cSocketThreads()
|
||||
{
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
delete *itr;
|
||||
} // for itr - m_Threads[]
|
||||
m_Threads.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::AddClient(cSocket * a_Socket, cCallback * a_Client)
|
||||
{
|
||||
// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client
|
||||
|
||||
// Try to add to existing threads:
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->IsValid() && (*itr)->HasEmptySlot())
|
||||
{
|
||||
(*itr)->AddClient(a_Socket, a_Client);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No thread has free space, create a new one:
|
||||
LOGD("Creating a new cSocketThread (currently have %d)", m_Threads.size());
|
||||
cSocketThread * Thread = new cSocketThread(this);
|
||||
if (!Thread->Start())
|
||||
{
|
||||
// There was an error launching the thread (but it was already logged along with the reason)
|
||||
LOGERROR("A new cSocketThread failed to start");
|
||||
delete Thread;
|
||||
return false;
|
||||
}
|
||||
Thread->AddClient(a_Socket, a_Client);
|
||||
m_Threads.push_back(Thread);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::RemoveClient(const cSocket * a_Socket)
|
||||
{
|
||||
// Remove the socket (and associated client) from processing
|
||||
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->RemoveSocket(a_Socket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for itr - m_Threads[]
|
||||
|
||||
// Cannot assert here, this may actually happen legally, since cClientHandle has to clean up the socket and it may have already closed in the meantime
|
||||
// ASSERT(!"Removing an unknown socket");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::RemoveClient(const cCallback * a_Client)
|
||||
{
|
||||
// Remove the associated socket and the client from processing
|
||||
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->RemoveClient(a_Client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for itr - m_Threads[]
|
||||
|
||||
ASSERT(!"Removing an unknown client");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::NotifyWrite(const cCallback * a_Client)
|
||||
{
|
||||
// Notifies the thread responsible for a_Client that the client has something to write
|
||||
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->NotifyWrite(a_Client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for itr - m_Threads[]
|
||||
|
||||
// Cannot assert - this normally happens if a client disconnects and has pending packets, the cServer::cNotifyWriteThread will call this on invalid clients too
|
||||
// ASSERT(!"Notifying write to an unknown client");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::Write(const cSocket * a_Socket, const AString & a_Data)
|
||||
{
|
||||
// Puts a_Data into outgoing data queue for a_Socket
|
||||
|
||||
if (!a_Socket->IsValid())
|
||||
{
|
||||
// Socket already closed, ignore the request
|
||||
return;
|
||||
}
|
||||
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->Write(a_Socket, a_Data))
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for itr - m_Threads[]
|
||||
|
||||
// This may be perfectly legal, if the socket has been destroyed and the client is finishing up
|
||||
// ASSERT(!"Writing to an unknown socket");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made
|
||||
void cSocketThreads::StopReading(const cCallback * a_Client)
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->StopReading(a_Client))
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for itr - m_Threads[]
|
||||
|
||||
// Cannot assert, this normally happens if the socket is closed before the client deinitializes
|
||||
// ASSERT(!"Stopping reading on an unknown client");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// Queues the socket for closing, as soon as its outgoing data is sent
|
||||
void cSocketThreads::QueueClose(const cSocket * a_Socket)
|
||||
{
|
||||
if (!a_Socket->IsValid())
|
||||
{
|
||||
// Already closed, ignore the request
|
||||
return;
|
||||
}
|
||||
|
||||
cCSLock Lock(m_CS);
|
||||
for (cSocketThreadList::iterator itr = m_Threads.begin(); itr != m_Threads.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->QueueClose(a_Socket))
|
||||
{
|
||||
return;
|
||||
}
|
||||
} // for itr - m_Threads[]
|
||||
|
||||
ASSERT(!"Queueing close of an unknown socket");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cSocketThreads::cSocketThread:
|
||||
|
||||
cSocketThreads::cSocketThread::cSocketThread(cSocketThreads * a_Parent) :
|
||||
cIsThread("cSocketThread"),
|
||||
m_Parent(a_Parent),
|
||||
m_NumSlots(0)
|
||||
{
|
||||
// Nothing needed yet
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cSocketThreads::cSocketThread::~cSocketThread()
|
||||
{
|
||||
m_ShouldTerminate = true;
|
||||
|
||||
// Notify the thread:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("a", 1);
|
||||
|
||||
// Wait for the thread to finish:
|
||||
Wait();
|
||||
|
||||
// Close the control sockets:
|
||||
m_ControlSocket1.CloseSocket();
|
||||
m_ControlSocket2.CloseSocket();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::cSocketThread::AddClient(cSocket * a_Socket, cCallback * a_Client)
|
||||
{
|
||||
ASSERT(m_NumSlots < MAX_SLOTS); // Use HasEmptySlot() to check before adding
|
||||
|
||||
m_Slots[m_NumSlots].m_Client = a_Client;
|
||||
m_Slots[m_NumSlots].m_Socket = a_Socket;
|
||||
m_Slots[m_NumSlots].m_Outgoing.clear();
|
||||
m_NumSlots++;
|
||||
|
||||
// Notify the thread of the change:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("a", 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::RemoveClient(const cCallback * a_Client)
|
||||
{
|
||||
// Returns true if removed, false if not found
|
||||
|
||||
if (m_NumSlots == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = m_NumSlots - 1; i >= 0 ; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Client != a_Client)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found, remove it:
|
||||
m_Slots[i] = m_Slots[--m_NumSlots];
|
||||
|
||||
// Notify the thread of the change:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("r", 1);
|
||||
return true;
|
||||
} // for i - m_Slots[]
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::RemoveSocket(const cSocket * a_Socket)
|
||||
{
|
||||
// Returns true if removed, false if not found
|
||||
|
||||
for (int i = m_NumSlots - 1; i >= 0 ; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Socket != a_Socket)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found, remove it:
|
||||
m_Slots[i] = m_Slots[--m_NumSlots];
|
||||
|
||||
// Notify the thread of the change:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("r", 1);
|
||||
return true;
|
||||
} // for i - m_Slots[]
|
||||
|
||||
// Not found
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::HasClient(const cCallback * a_Client) const
|
||||
{
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Client == a_Client)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::HasSocket(const cSocket * a_Socket) const
|
||||
{
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Socket->GetSocket() == a_Socket->GetSocket())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::NotifyWrite(const cCallback * a_Client)
|
||||
{
|
||||
if (HasClient(a_Client))
|
||||
{
|
||||
// Notify the thread that there's another packet in the queue:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("q", 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::Write(const cSocket * a_Socket, const AString & a_Data)
|
||||
{
|
||||
// Returns true if socket handled by this thread
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Socket == a_Socket)
|
||||
{
|
||||
m_Slots[i].m_Outgoing.append(a_Data);
|
||||
|
||||
// Notify the thread that there's data in the queue:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("q", 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::StopReading (const cCallback * a_Client)
|
||||
{
|
||||
// Returns true if client handled by this thread
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Client == a_Client)
|
||||
{
|
||||
m_Slots[i].m_Client = NULL;
|
||||
m_Slots[i].m_ShouldClose = false;
|
||||
|
||||
// Notify the thread that there's a stop reading request:
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("s", 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::QueueClose(const cSocket * a_Socket)
|
||||
{
|
||||
// Returns true if socket handled by this thread
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Socket == a_Socket)
|
||||
{
|
||||
ASSERT(m_Slots[i].m_Client == NULL); // Should have stopped reading first
|
||||
m_Slots[i].m_ShouldClose = true;
|
||||
|
||||
// Notify the thread that there's a close queued (in case its conditions are already met):
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("c", 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cSocketThreads::cSocketThread::Start(void)
|
||||
{
|
||||
// Create the control socket listener
|
||||
m_ControlSocket2 = cSocket::CreateSocket();
|
||||
if (!m_ControlSocket2.IsValid())
|
||||
{
|
||||
LOGERROR("Cannot create a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
|
||||
return false;
|
||||
}
|
||||
cSocket::SockAddr_In Addr;
|
||||
Addr.Family = cSocket::ADDRESS_FAMILY_INTERNET;
|
||||
Addr.Address = cSocket::INTERNET_ADDRESS_LOCALHOST();
|
||||
Addr.Port = 0; // Any free port is okay
|
||||
if (m_ControlSocket2.Bind(Addr) != 0)
|
||||
{
|
||||
LOGERROR("Cannot bind a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
|
||||
m_ControlSocket2.CloseSocket();
|
||||
return false;
|
||||
}
|
||||
if (m_ControlSocket2.Listen(1) != 0)
|
||||
{
|
||||
LOGERROR("Cannot listen on a Control socket for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
|
||||
m_ControlSocket2.CloseSocket();
|
||||
return false;
|
||||
}
|
||||
if (m_ControlSocket2.GetPort() == 0)
|
||||
{
|
||||
LOGERROR("Cannot determine Control socket port (\"%s\"); conitnuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
|
||||
m_ControlSocket2.CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start the thread
|
||||
if (!super::Start())
|
||||
{
|
||||
LOGERROR("Cannot start new cSocketThread");
|
||||
m_ControlSocket2.CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finish connecting the control socket by accepting connection from the thread's socket
|
||||
cSocket tmp = m_ControlSocket2.Accept();
|
||||
if (!tmp.IsValid())
|
||||
{
|
||||
LOGERROR("Cannot link Control sockets for a cSocketThread (\"%s\"); continuing, but server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
|
||||
m_ControlSocket2.CloseSocket();
|
||||
return false;
|
||||
}
|
||||
m_ControlSocket2.CloseSocket();
|
||||
m_ControlSocket2 = tmp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::cSocketThread::Execute(void)
|
||||
{
|
||||
// Connect the "client" part of the Control socket:
|
||||
m_ControlSocket1 = cSocket::CreateSocket();
|
||||
cSocket::SockAddr_In Addr;
|
||||
Addr.Family = cSocket::ADDRESS_FAMILY_INTERNET;
|
||||
Addr.Address = cSocket::INTERNET_ADDRESS_LOCALHOST();
|
||||
Addr.Port = m_ControlSocket2.GetPort();
|
||||
ASSERT(Addr.Port != 0); // We checked in the Start() method, but let's be sure
|
||||
if (m_ControlSocket1.Connect(Addr) != 0)
|
||||
{
|
||||
LOGERROR("Cannot connect Control sockets for a cSocketThread (\"%s\"); continuing, but the server may be unreachable from now on.", cSocket::GetLastErrorString().c_str());
|
||||
m_ControlSocket2.CloseSocket();
|
||||
return;
|
||||
}
|
||||
|
||||
// The main thread loop:
|
||||
while (!m_ShouldTerminate)
|
||||
{
|
||||
// Put all sockets into the Read set:
|
||||
fd_set fdRead;
|
||||
cSocket::xSocket Highest = m_ControlSocket1.GetSocket();
|
||||
|
||||
PrepareSet(&fdRead, Highest);
|
||||
|
||||
// Wait for the sockets:
|
||||
if (select(Highest + 1, &fdRead, NULL, NULL, NULL) == -1)
|
||||
{
|
||||
LOG("select(R) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
ReadFromSockets(&fdRead);
|
||||
|
||||
// Test sockets for writing:
|
||||
fd_set fdWrite;
|
||||
Highest = m_ControlSocket1.GetSocket();
|
||||
PrepareSet(&fdWrite, Highest);
|
||||
timeval Timeout;
|
||||
Timeout.tv_sec = 0;
|
||||
Timeout.tv_usec = 0;
|
||||
if (select(Highest + 1, NULL, &fdWrite, NULL, &Timeout) == -1)
|
||||
{
|
||||
LOG("select(W) call failed in cSocketThread: \"%s\"", cSocket::GetLastErrorString().c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
WriteToSockets(&fdWrite);
|
||||
|
||||
RemoveClosedSockets();
|
||||
} // while (!mShouldTerminate)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::cSocketThread::PrepareSet(fd_set * a_Set, cSocket::xSocket & a_Highest)
|
||||
{
|
||||
FD_ZERO(a_Set);
|
||||
FD_SET(m_ControlSocket1.GetSocket(), a_Set);
|
||||
|
||||
cCSLock Lock(m_Parent->m_CS);
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (!m_Slots[i].m_Socket->IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
cSocket::xSocket s = m_Slots[i].m_Socket->GetSocket();
|
||||
FD_SET(s, a_Set);
|
||||
if (s > a_Highest)
|
||||
{
|
||||
a_Highest = s;
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::cSocketThread::ReadFromSockets(fd_set * a_Read)
|
||||
{
|
||||
// Read on available sockets:
|
||||
|
||||
// Reset Control socket state:
|
||||
if (FD_ISSET(m_ControlSocket1.GetSocket(), a_Read))
|
||||
{
|
||||
char Dummy[128];
|
||||
m_ControlSocket1.Receive(Dummy, sizeof(Dummy), 0);
|
||||
}
|
||||
|
||||
// Read from clients:
|
||||
cCSLock Lock(m_Parent->m_CS);
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (!FD_ISSET(m_Slots[i].m_Socket->GetSocket(), a_Read))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
char Buffer[1024];
|
||||
int Received = m_Slots[i].m_Socket->Receive(Buffer, ARRAYCOUNT(Buffer), 0);
|
||||
if (Received == 0)
|
||||
{
|
||||
// The socket has been closed by the remote party, close our socket and let it be removed after we process all reading
|
||||
m_Slots[i].m_Socket->CloseSocket();
|
||||
if (m_Slots[i].m_Client != NULL)
|
||||
{
|
||||
m_Slots[i].m_Client->SocketClosed();
|
||||
}
|
||||
}
|
||||
else if (Received > 0)
|
||||
{
|
||||
if (m_Slots[i].m_Client != NULL)
|
||||
{
|
||||
m_Slots[i].m_Client->DataReceived(Buffer, Received);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The socket has encountered an error, close it and let it be removed after we process all reading
|
||||
m_Slots[i].m_Socket->CloseSocket();
|
||||
if (m_Slots[i].m_Client != NULL)
|
||||
{
|
||||
m_Slots[i].m_Client->SocketClosed();
|
||||
}
|
||||
}
|
||||
} // for i - m_Slots[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::cSocketThread::WriteToSockets(fd_set * a_Write)
|
||||
{
|
||||
// Write to available client sockets:
|
||||
cCSLock Lock(m_Parent->m_CS);
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
cSocket Socket(*(m_Slots[i].m_Socket));
|
||||
if (!Socket.IsValid() || !FD_ISSET(Socket.GetSocket(), a_Write))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (m_Slots[i].m_Outgoing.empty())
|
||||
{
|
||||
// Request another chunk of outgoing data:
|
||||
if (m_Slots[i].m_Client != NULL)
|
||||
{
|
||||
m_Slots[i].m_Client->GetOutgoingData(m_Slots[i].m_Outgoing);
|
||||
}
|
||||
if (m_Slots[i].m_Outgoing.empty())
|
||||
{
|
||||
// Nothing ready
|
||||
if ((m_Slots[i].m_Client == NULL) && m_Slots[i].m_ShouldClose)
|
||||
{
|
||||
// Socket was queued for closing and there's no more data to send, close it now:
|
||||
m_Slots[i].m_Socket->CloseSocket();
|
||||
m_Slots[i] = m_Slots[--m_NumSlots];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} // if (outgoing data is empty)
|
||||
|
||||
int Sent = m_Slots[i].m_Socket->Send(m_Slots[i].m_Outgoing.data(), m_Slots[i].m_Outgoing.size());
|
||||
if (Sent < 0)
|
||||
{
|
||||
int Err = cSocket::GetLastError();
|
||||
LOGWARNING("Error %d while writing to client \"%s\", disconnecting. \"%s\"", Err, m_Slots[i].m_Socket->GetIPString().c_str(), cSocket::GetErrorString(Err).c_str());
|
||||
m_Slots[i].m_Socket->CloseSocket();
|
||||
if (m_Slots[i].m_Client != NULL)
|
||||
{
|
||||
m_Slots[i].m_Client->SocketClosed();
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_Slots[i].m_Outgoing.erase(0, Sent);
|
||||
|
||||
// _X: If there's data left, it means the client is not reading fast enough, the server would unnecessarily spin in the main loop with zero actions taken; so signalling is disabled
|
||||
// This means that if there's data left, it will be sent only when there's incoming data or someone queues another packet (for any socket handled by this thread)
|
||||
/*
|
||||
// If there's any data left, signalize the Control socket:
|
||||
if (!m_Slots[i].m_Outgoing.empty())
|
||||
{
|
||||
ASSERT(m_ControlSocket2.IsValid());
|
||||
m_ControlSocket2.Send("q", 1);
|
||||
}
|
||||
*/
|
||||
} // for i - m_Slots[i]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cSocketThreads::cSocketThread::RemoveClosedSockets(void)
|
||||
{
|
||||
// Removes sockets that have closed from m_Slots[]
|
||||
|
||||
cCSLock Lock(m_Parent->m_CS);
|
||||
for (int i = m_NumSlots - 1; i >= 0; --i)
|
||||
{
|
||||
if (m_Slots[i].m_Socket->IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
m_Slots[i] = m_Slots[--m_NumSlots];
|
||||
} // for i - m_Slots[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
|
||||
// SocketThreads.h
|
||||
|
||||
// Interfaces to the cSocketThreads class representing the heart of MCS's client networking.
|
||||
// This object takes care of network communication, groups sockets into threads and uses as little threads as possible for full read / write support
|
||||
// For more detail, see http://forum.mc-server.org/showthread.php?tid=327
|
||||
|
||||
/*
|
||||
Additional details:
|
||||
When a client is terminating a connection:
|
||||
- they call the StopReading() method to disable callbacks for the incoming data
|
||||
- they call the Write() method to queue any outstanding outgoing data
|
||||
- they call the QueueClose() method to queue the socket to close after outgoing data has been sent.
|
||||
When a socket slot is marked as having no callback, it is kept alive until its outgoing data queue is empty and its m_ShouldClose flag is set.
|
||||
This means that the socket can be written to several times before finally closing it via QueueClose()
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// How many clients should one thread handle? (must be less than FD_SETSIZE for your platform)
|
||||
#define MAX_SLOTS 63
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
#ifndef CSOCKETTHREADS_H_INCLUDED
|
||||
#define CSOCKETTHREADS_H_INCLUDED
|
||||
|
||||
#include "Socket.h"
|
||||
#include "IsThread.h"
|
||||
|
||||
|
||||
|
||||
|
||||
// Check MAX_SLOTS:
|
||||
#if MAX_SLOTS >= FD_SETSIZE
|
||||
#error "MAX_SLOTS must be less than FD_SETSIZE for your platform! (otherwise select() won't work)"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd:
|
||||
class cSocket;
|
||||
class cClientHandle;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cSocketThreads
|
||||
{
|
||||
public:
|
||||
|
||||
// Clients of cSocketThreads must implement this interface to be able to communicate
|
||||
class cCallback
|
||||
{
|
||||
public:
|
||||
/// Called when data is received from the remote party
|
||||
virtual void DataReceived(const char * a_Data, int a_Size) = 0;
|
||||
|
||||
/// Called when data can be sent to remote party; the function is supposed to append outgoing data to a_Data
|
||||
virtual void GetOutgoingData(AString & a_Data) = 0;
|
||||
|
||||
/// Called when the socket has been closed for any reason
|
||||
virtual void SocketClosed(void) = 0;
|
||||
} ;
|
||||
|
||||
|
||||
cSocketThreads(void);
|
||||
~cSocketThreads();
|
||||
|
||||
/// Add a (socket, client) pair for processing, data from a_Socket is to be sent to a_Client; returns true if successful
|
||||
bool AddClient(cSocket * a_Socket, cCallback * a_Client);
|
||||
|
||||
/// Remove the socket (and associated client) from processing
|
||||
void RemoveClient(const cSocket * a_Socket);
|
||||
|
||||
/// Remove the associated socket and the client from processing
|
||||
void RemoveClient(const cCallback * a_Client);
|
||||
|
||||
/// Notify the thread responsible for a_Client that the client has something to write
|
||||
void NotifyWrite(const cCallback * a_Client);
|
||||
|
||||
/// Puts a_Data into outgoing data queue for a_Socket
|
||||
void Write(const cSocket * a_Socket, const AString & a_Data);
|
||||
|
||||
/// Stops reading from the socket - when this call returns, no more calls to the callbacks are made
|
||||
void StopReading(const cCallback * a_Client);
|
||||
|
||||
/// Queues the socket for closing, as soon as its outgoing data is sent
|
||||
void QueueClose(const cSocket * a_Socket);
|
||||
|
||||
private:
|
||||
|
||||
class cSocketThread :
|
||||
public cIsThread
|
||||
{
|
||||
typedef cIsThread super;
|
||||
|
||||
public:
|
||||
|
||||
cSocketThread(cSocketThreads * a_Parent);
|
||||
~cSocketThread();
|
||||
|
||||
// All these methods assume parent's m_CS is locked
|
||||
bool HasEmptySlot(void) const {return m_NumSlots < MAX_SLOTS; }
|
||||
bool IsEmpty (void) const {return m_NumSlots == 0; }
|
||||
|
||||
void AddClient (cSocket * a_Socket, cCallback * a_Client);
|
||||
bool RemoveClient(const cCallback * a_Client); // Returns true if removed, false if not found
|
||||
bool RemoveSocket(const cSocket * a_Socket); // Returns true if removed, false if not found
|
||||
bool HasClient (const cCallback * a_Client) const;
|
||||
bool HasSocket (const cSocket * a_Socket) const;
|
||||
bool NotifyWrite (const cCallback * a_Client); // Returns true if client handled by this thread
|
||||
bool Write (const cSocket * a_Socket, const AString & a_Data); // Returns true if socket handled by this thread
|
||||
bool StopReading (const cCallback * a_Client); // Returns true if client handled by this thread
|
||||
bool QueueClose (const cSocket * a_Socket); // Returns true if socket handled by this thread
|
||||
|
||||
bool Start(void); // Hide the cIsThread's Start method, we need to provide our own startup to create the control socket
|
||||
|
||||
bool IsValid(void) const {return m_ControlSocket2.IsValid(); } // If the Control socket dies, the thread is not valid anymore
|
||||
|
||||
private:
|
||||
|
||||
cSocketThreads * m_Parent;
|
||||
|
||||
// Two ends of the control socket, the first is select()-ed, the second is written to for notifications
|
||||
cSocket m_ControlSocket1;
|
||||
cSocket m_ControlSocket2;
|
||||
|
||||
// Socket-client-packetqueues triplets.
|
||||
// Manipulation with these assumes that the parent's m_CS is locked
|
||||
struct sSlot
|
||||
{
|
||||
cSocket * m_Socket;
|
||||
cCallback * m_Client;
|
||||
AString m_Outgoing; // If sending writes only partial data, the rest is stored here for another send
|
||||
bool m_ShouldClose; // If true, the socket is to be closed after sending all outgoing data
|
||||
} ;
|
||||
sSlot m_Slots[MAX_SLOTS];
|
||||
int m_NumSlots; // Number of slots actually used
|
||||
|
||||
virtual void Execute(void) override;
|
||||
|
||||
void PrepareSet (fd_set * a_Set, cSocket::xSocket & a_Highest); // Puts all sockets into the set, along with m_ControlSocket1
|
||||
void ReadFromSockets(fd_set * a_Read); // Reads from sockets indicated in a_Read
|
||||
void WriteToSockets (fd_set * a_Write); // Writes to sockets indicated in a_Write
|
||||
void RemoveClosedSockets(void); // Removes sockets that have closed from m_Slots[]
|
||||
} ;
|
||||
|
||||
typedef std::list<cSocketThread *> cSocketThreadList;
|
||||
|
||||
|
||||
cCriticalSection m_CS;
|
||||
cSocketThreadList m_Threads;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif // CSOCKETTHREADS_H_INCLUDED
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "TCPLink.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
#ifdef __MACH__
|
||||
#define MSG_NOSIGNAL (0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cTCPLink::cTCPLink()
|
||||
: m_Socket( 0 )
|
||||
, m_StopEvent( new cEvent() )
|
||||
{
|
||||
}
|
||||
|
||||
cTCPLink::~cTCPLink()
|
||||
{
|
||||
if( m_Socket )
|
||||
{
|
||||
CloseSocket();
|
||||
m_StopEvent->Wait();
|
||||
}
|
||||
delete m_StopEvent;
|
||||
}
|
||||
|
||||
void cTCPLink::CloseSocket()
|
||||
{
|
||||
if( m_Socket )
|
||||
{
|
||||
m_Socket.CloseSocket();
|
||||
m_Socket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool cTCPLink::Connect( const AString & a_Address, unsigned int a_Port )
|
||||
{
|
||||
if( m_Socket )
|
||||
{
|
||||
LOGWARN("WARNING: cTCPLink Connect() called while still connected. ALWAYS disconnect before re-connecting!");
|
||||
}
|
||||
|
||||
m_Socket = cSocket::CreateSocket();
|
||||
if( !m_Socket.IsValid() )
|
||||
{
|
||||
LOGERROR("cTCPLink: Failed to create socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_Socket.Connect(a_Address, a_Port) != 0)
|
||||
{
|
||||
LOGWARN("cTCPLink: Cannot connect to server \"%s\" (%s)", m_Socket.GetLastErrorString().c_str());
|
||||
m_Socket.CloseSocket();
|
||||
return false;
|
||||
}
|
||||
|
||||
cThread( ReceiveThread, this );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cTCPLink::Send(const char * a_Data, unsigned int a_Size, int a_Flags /* = 0 */ )
|
||||
{
|
||||
(void)a_Flags;
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
LOGWARN("cTCPLink: Trying to send data without a valid connection!");
|
||||
return -1;
|
||||
}
|
||||
return m_Socket.Send(a_Data, a_Size);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
int cTCPLink::SendMessage(const char * a_Message, int a_Flags /* = 0 */ )
|
||||
{
|
||||
(void)a_Flags;
|
||||
if (!m_Socket.IsValid())
|
||||
{
|
||||
LOGWARN("cTCPLink: Trying to send message without a valid connection!");
|
||||
return -1;
|
||||
}
|
||||
return m_Socket.Send(a_Message, strlen(a_Message));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cTCPLink::ReceiveThread( void* a_Param)
|
||||
{
|
||||
cTCPLink* self = (cTCPLink*)a_Param;
|
||||
cSocket Socket = self->m_Socket;
|
||||
int Received = 0;
|
||||
do
|
||||
{
|
||||
char Data[256];
|
||||
Received = Socket.Receive(Data, sizeof(Data), 0);
|
||||
self->ReceivedData( Data, ((Received > 0) ? Received : -1) );
|
||||
} while ( Received > 0 );
|
||||
|
||||
LOGINFO("cTCPLink Disconnected (%i)", Received );
|
||||
|
||||
if (Socket == self->m_Socket)
|
||||
{
|
||||
self->m_StopEvent->Set();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "Socket.h"
|
||||
|
||||
class cTCPLink //tolua_export
|
||||
{ //tolua_export
|
||||
public: //tolua_export
|
||||
cTCPLink(); //tolua_export
|
||||
~cTCPLink(); //tolua_export
|
||||
|
||||
bool Connect (const AString & a_Address, unsigned int a_Port ); //tolua_export
|
||||
int Send (const char * a_Data, unsigned int a_Size, int a_Flags = 0 ); //tolua_export
|
||||
int SendMessage(const char * a_Message, int a_Flags = 0 ); //tolua_export
|
||||
void CloseSocket(); //tolua_export
|
||||
protected: //tolua_export
|
||||
virtual void ReceivedData( char a_Data[256], int a_Size ) = 0; //tolua_export
|
||||
|
||||
static void ReceiveThread( void* a_Param );
|
||||
|
||||
cSocket m_Socket;
|
||||
cEvent* m_StopEvent;
|
||||
}; //tolua_export
|
||||
@@ -0,0 +1,128 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// When in MSVC, the debugger provides "thread naming" by catching special exceptions. Interface here:
|
||||
#ifdef _MSC_VER
|
||||
//
|
||||
// Usage: SetThreadName (-1, "MainThread");
|
||||
//
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // must be 0x1000
|
||||
LPCSTR szName; // pointer to name (in user addr space)
|
||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||
DWORD dwFlags; // reserved for future use, must be zero
|
||||
} THREADNAME_INFO;
|
||||
|
||||
void SetThreadName( DWORD dwThreadID, LPCSTR szThreadName)
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = szThreadName;
|
||||
info.dwThreadID = dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
|
||||
__try
|
||||
{
|
||||
RaiseException( 0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info );
|
||||
}
|
||||
__except(EXCEPTION_CONTINUE_EXECUTION)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif // _MSC_VER
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cThread::cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName /* = 0 */ )
|
||||
: m_ThreadFunction( a_ThreadFunction )
|
||||
, m_Param( a_Param )
|
||||
, m_Event( new cEvent() )
|
||||
, m_StopEvent( 0 )
|
||||
{
|
||||
if( a_ThreadName )
|
||||
{
|
||||
m_ThreadName.assign(a_ThreadName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cThread::~cThread()
|
||||
{
|
||||
delete m_Event;
|
||||
|
||||
if( m_StopEvent )
|
||||
{
|
||||
m_StopEvent->Wait();
|
||||
delete m_StopEvent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cThread::Start( bool a_bWaitOnDelete /* = true */ )
|
||||
{
|
||||
if( a_bWaitOnDelete )
|
||||
m_StopEvent = new cEvent();
|
||||
|
||||
#ifndef _WIN32
|
||||
pthread_t SndThread;
|
||||
if( pthread_create( &SndThread, NULL, MyThread, this) )
|
||||
LOGERROR("ERROR: Could not create thread!");
|
||||
#else
|
||||
DWORD ThreadID = 0;
|
||||
HANDLE hThread = CreateThread( 0 // security
|
||||
,0 // stack size
|
||||
, (LPTHREAD_START_ROUTINE) MyThread // function name
|
||||
,this // parameters
|
||||
,0 // flags
|
||||
,&ThreadID ); // thread id
|
||||
CloseHandle( hThread );
|
||||
|
||||
#ifdef _MSC_VER
|
||||
if (!m_ThreadName.empty())
|
||||
{
|
||||
SetThreadName(ThreadID, m_ThreadName.c_str());
|
||||
}
|
||||
#endif // _MSC_VER
|
||||
#endif
|
||||
|
||||
// Wait until thread has actually been created
|
||||
m_Event->Wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef _WIN32
|
||||
unsigned long cThread::MyThread(void* a_Param )
|
||||
#else
|
||||
void *cThread::MyThread( void *a_Param )
|
||||
#endif
|
||||
{
|
||||
cThread* self = (cThread*)a_Param;
|
||||
cEvent* StopEvent = self->m_StopEvent;
|
||||
|
||||
ThreadFunc* ThreadFunction = self->m_ThreadFunction;
|
||||
void* ThreadParam = self->m_Param;
|
||||
|
||||
// Set event to let other thread know this thread has been created and it's safe to delete the cThread object
|
||||
self->m_Event->Set();
|
||||
|
||||
ThreadFunction( ThreadParam );
|
||||
|
||||
if( StopEvent ) StopEvent->Set();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
class cThread
|
||||
{
|
||||
public:
|
||||
typedef void (ThreadFunc)(void*);
|
||||
cThread( ThreadFunc a_ThreadFunction, void* a_Param, const char* a_ThreadName = 0 );
|
||||
~cThread();
|
||||
|
||||
void Start( bool a_bWaitOnDelete = true );
|
||||
void WaitForThread();
|
||||
private:
|
||||
ThreadFunc* m_ThreadFunction;
|
||||
|
||||
#ifdef _WIN32
|
||||
static unsigned long MyThread(void* a_Param );
|
||||
#else
|
||||
static void *MyThread( void *lpParam );
|
||||
#endif
|
||||
|
||||
void* m_Param;
|
||||
cEvent* m_Event;
|
||||
cEvent* m_StopEvent;
|
||||
|
||||
AString m_ThreadName;
|
||||
};
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Timer.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
cTimer::cTimer()
|
||||
#ifdef _WIN32
|
||||
: m_TicksPerSecond( new LARGE_INTEGER )
|
||||
#endif
|
||||
{
|
||||
#ifdef _WIN32
|
||||
QueryPerformanceFrequency( (LARGE_INTEGER*)m_TicksPerSecond );
|
||||
#endif
|
||||
}
|
||||
|
||||
cTimer::~cTimer()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
delete (LARGE_INTEGER*)m_TicksPerSecond;
|
||||
#endif
|
||||
}
|
||||
|
||||
long long cTimer::GetNowTime()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
LARGE_INTEGER now;
|
||||
QueryPerformanceCounter( &now );
|
||||
LARGE_INTEGER & tps = *((LARGE_INTEGER*)m_TicksPerSecond);
|
||||
return ((now.QuadPart*1000) / tps.QuadPart );
|
||||
#else
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
return (long long)(now.tv_sec*1000 + now.tv_usec/1000);
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
class cTimer
|
||||
{
|
||||
public:
|
||||
cTimer();
|
||||
~cTimer();
|
||||
|
||||
long long GetNowTime();
|
||||
private:
|
||||
|
||||
#ifdef _WIN32
|
||||
void* m_TicksPerSecond; // LARGE_INTEGER*
|
||||
#endif
|
||||
};
|
||||
Reference in New Issue
Block a user