Index: src/realmd/Main.cpp =================================================================== --- src/realmd/Main.cpp (revision 23) +++ src/realmd/Main.cpp (working copy) @@ -256,10 +256,10 @@ ///- Wait for termination signal ListenSocket authListenSocket("0.0.0.0", rmport); - if(authListenSocket.IsListernPortOpened()) + if(authListenSocket.IsListenPortOpened()) sSocketManager.StartSocketThreads(); ///- Wait for termination signal or listen socket closing - while (!stopEvent && authListenSocket.IsListernPortOpened()) + while (!stopEvent && authListenSocket.IsListenPortOpened()) { Sleep(time_to_sleep); if (RestToPing - time_to_sleep < 0) Index: src/realmd/realmd.conf =================================================================== --- src/realmd/realmd.conf (revision 23) +++ src/realmd/realmd.conf (working copy) @@ -1,58 +1,103 @@ -# MaNGOS realmd configuration file +############################################ +# MaNGOS realmd configuration file # +############################################ ConfVersion=2007062001 -# Database connection settings for the realm server. -# Default: hostname;port;username;password;database -# .;somenumber;username;password;database - use named pipes at Windows -# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini -# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux -# Unix sockets: experimental, not tested -LoginDatabaseInfo = "127.0.0.1;3306;mangos;mangos;realmd" +################################################################################################################### +# REALMD SETTINGS +# +# LoginDatabaseInfo +# Database connection settings for the realm server. +# Default: hostname;port;username;password;database +# .;somenumber;username;password;database - use named pipes at Windows +# Named pipes: mySQL required adding "enable-named-pipe" to [mysqld] section my.ini +# .;/path/to/unix_socket;username;password;database - use Unix sockets at Unix/Linux +# Unix sockets: experimental, not tested +# +# LogsDir +# Logs directory setting. +# Important: Logs dir must exists, or all logs be disable +# Default: "" - no log directory prefix, if used log names isn't absolute path then logs will be +# stored in current directory for run program. +# +# MaxPingTime +# Settings for maximum database-ping interval (minutes between pings) +# +# RealmServerPort +# Default RealmServerPort +# +# BindIP +# Bind Realm Server to IP/hostname +# +# PidFile +# Realmd daemon PID file +# Default: "" - do not create PID file +# "./realmd.pid" - create PID file (recommended name) +# +# LogLevel +# Server console level of logging +# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug +# Default: 0 +# +# LogTime +# Include time in server console output [hh:mm:ss] +# Default: 0 (no time) +# 1 (print time) +# +# LogFile +# Logfile name +# Default: "Realmd.log" +# "" - empty name disable creating log file +# +# LogTimestamp +# Logfile with timestamp of server start in name +# Default: 0 - no timestamp in name +# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext +# +# LogFileLevel +# Server file level of logging +# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug +# Default: 0 +# +# LogColors +# Color for messages (format "normal_color details_color debug_color error_color) +# Colors: 0 - BLACK, 1 - RED, 2 - GREEN, 3 - BROWN, 4 - BLUE, 5 - MAGENTA, 6 - CYAN, 7 - GREY, +# 8 - YELLOW, 9 - LRED, 10 - LGREEN, 11 - LBLUE, 12 - LMAGENTA, 13 - LCYAN, 14 - WHITE +# Default: "" - none colors +# "13 7 11 9" - for example :) +# +# UseProcessors +# Used processors mask for multi-processors system (Used only at Windows) +# Default: 0 (selected by OS) +# number (bitmask value of selected processors) +# +# ProcessPriority +# Process proirity setting (Used only at Windows) +# Default: 1 (HIGH) +# 0 (Normal) +# +# RealmsStateUpdateDelay +# Realm list Update up delay (updated at realm list request if delay expired). +# Default: 20 +# 0 (Disabled) +# +# +# +# +################################################################################################################### -# Logs directory setting. -# Important: Logs dir must exists, or all logs be disable -# Default: "" - no log directory prefix, if used log names isn't absolute path then logs will be stored in current directory for run program. +LoginDatabaseInfo = "127.0.0.1;3306;root;mangos;realmd" LogsDir = "" - -# Settings for maximum database-ping interval (minutes between pings) MaxPingTime = 30 - -# Default RealmServerPort RealmServerPort = 3724 - -# Server console level of logging -# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug -# Default: 0 +BindIP = "0.0.0.0" +PidFile = "" LogLevel = 0 - -# Logfile name -# empty name disable creating log file: LogFile = -# Default: "Server.log" - not recommended. +LogTime = 0 LogFile = "Realmd.log" - -# Logfile with timestamp of server start in name -# Default: 0 - no timestamp in name -# 1 - add timestamp in name in form Logname_YYYY-MM-DD_HH-MM-SS.Ext for Logname.Ext LogTimestamp = 0 - -# Server file level of logging -# 0 = Minimum; 1 = Error; 2 = Detail; 3 = Full/Debug -# Default: 0 LogFileLevel = 0 - -# Color for messages (format "normal_color details_color debug_color error_color) -# Colors: 0 - BLACK, 1 - RED, 2 - GREEN, 3 - BROWN, 4 - BLUE, 5 - MAGENTA, 6 - CYAN, 7 - GREY, -# 8 - YELLOW, 9 - LRED, 10 - LGREEN, 11 - LBLUE, 12 - LMAGENTA, 13 - LCYAN, 14 - WHITE -# Default: "" - none colors -# "13 7 11 9" - for example :) LogColors = "" - -# Used processors mask for multi-processors system (Used only at Windows) -# Default: 0 (selected by OS) -# number (bitmask value of selected processors) UseProcessors = 0 - -# Process proirity setting (Used only at Windows) -# 0 (Normal) -# Default: 1 (HIGH) ProcessPriority = 1 +RealmsStateUpdateDelay = 20 Index: src/realmd/Makefile.am =================================================================== --- src/realmd/Makefile.am (revision 23) +++ src/realmd/Makefile.am (working copy) @@ -30,7 +30,7 @@ RealmList.h ## Link realm list daemon against the shared library -mangos_realmd_LDADD = ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/Network/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la +mangos_realmd_LDADD = ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/NetworkNew/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la mangos_realmd_LDFLAGS = -L../../dep/src/zthread $(MYSQL_LIBS) $(LEVENT_LIB) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) ## Additional files to include when running 'make dist' Index: src/shared/NetworkNew/Network.h =================================================================== --- src/shared/NetworkNew/Network.h (revision 0) +++ src/shared/NetworkNew/Network.h (revision 0) @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __NETWORK_H__ +#define __NETWORK_H__ + +#include "Common.h" +#include "Socket.h" +#include "SocketManager.h" +#endif//__NETWORK_H__ Index: src/shared/NetworkNew/Mutex.cpp =================================================================== --- src/shared/NetworkNew/Mutex.cpp (revision 0) +++ src/shared/NetworkNew/Mutex.cpp (revision 0) @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "Common.h" +#include "Mutex.h" + +#ifdef WIN32 +#include + +Mutex::Mutex(MutexType type) +{ + /*The InitializeCriticalSection function initializes + a critical section object.*/ + ::InitializeCriticalSection(&m_cs); +} + +Mutex::~Mutex() +{ + /*The DeleteCriticalSection function releases all + resources used by an unowned critical section object.*/ + ::DeleteCriticalSection(&m_cs); +} + +void Mutex::Lock() +{ + /*The EnterCriticalSection function waits for ownership of + the specified critical section object. The function returns + when the calling thread is granted ownership.*/ + ::EnterCriticalSection(&m_cs); +} + +void Mutex::Unlock() +{ + /*The LeaveCriticalSection function releases ownership of + the specified critical section object.*/ + ::LeaveCriticalSection(&m_cs); +} + +bool Mutex::TryLock() +{ + #if(_WIN32_WINNT >= 0x0400) + /*The TryEnterCriticalSection function attempts to enter a + critical section without blocking. If the call is successful, + the calling thread takes ownership of the critical section.*/ + return ::TryEnterCriticalSection(&m_cs); + #else + return false; + #endif +} + +#else +/* +Was used pthreadcc Library Version 1.01 +------------------------------ +Author: Jason But +jason.but@eng.monash.edu.au +*/ + +/******************************************************************************/ +/* Mutex Constructor. */ +/* */ +/* We must create the internal MutEx of the right type. Based on the setup */ +/* parameter type, we set the mutexTheMutEx variable to be initialized with */ +/* the appropriate type parameters. */ +/******************************************************************************/ +Mutex::Mutex(MutexType type) +{ + pthread_mutexattr_t mattrDetails; + pthread_mutexattr_init(&mattrDetails); + switch (type) + { + case fastMutEx: pthread_mutexattr_settype(&mattrDetails, PTHREAD_MUTEX_NORMAL); + break; + case recursiveMutEx: pthread_mutexattr_settype(&mattrDetails, PTHREAD_MUTEX_RECURSIVE); + break; + case errorcheckMutEx: pthread_mutexattr_settype(&mattrDetails, PTHREAD_MUTEX_ERRORCHECK); + break; + } + pthread_mutex_init(&m_mutex, &mattrDetails); + pthread_mutexattr_destroy(&mattrDetails); +} + +/******************************************************************************/ +/* Mutex Destructor. */ +/* */ +/* The class cannot be destroyed until the internal MutEx is also destroyed. */ +/* We do this by calling pthread_mutex_destroy() until it returns a success */ +/* code. If the MutEx is currently locked then we attempt to lock the MutEx */ +/* to wait for it to become free, we then immediately Unlock the mutex and */ +/* try to destroy it again. */ +/******************************************************************************/ +Mutex::~Mutex() +{ + while (pthread_mutex_destroy(&m_mutex) == EBUSY) + { + Lock(); + Unlock(); + } +} + +/******************************************************************************/ +/* void Lock() */ +/* */ +/* This method attempts to lock the MutEx and returns true if successful, or */ +/* throws an exception on error. If the MutEx is of an error checking type */ +/* and the MutEx is already locked by the calling thread, a MutexWouldDeadlock*/ +/* exception is thrown. The lock is done by calling the pthread_mutex_lock() */ +/* function. If 0 is returned, the MutEx is now locked and we can return, if */ +/* EDEADLK is returned then we cannot lock the error checking MutEx without */ +/* causing a deadlock and we throw the exception. Any other return values */ +/* signifies either an unknown error type or that the MutEx has not been */ +/* properly initialized, which CANNOT happen within this class, an Unknown */ +/* MutEx exception is thrown. */ +/******************************************************************************/ +void Mutex::Lock() +{ + pthread_mutex_lock(&m_mutex); + /* + case 0: return; + case EDEADLK: throw ThreadException(ThreadException::errMutexWouldDeadlock); + default: throw ThreadException(ThreadException::errMutexUnknown); + */ +} + +/******************************************************************************/ +/* void UnLock() */ +/* */ +/* This method attempts to unlock the MutEx and returns if successful, or */ +/* throws an exception on error. If the MutEx is of an error checking type */ +/* and the calling thread did not lock the MutEx, a MutExNotOwned exception */ +/* is thrown. This is done by calling the pthread_mutex_unlock() function. */ +/* If 0 is returned, the MutEx is now unlocked and we can return, if EPERM is */ +/* returned then we cannot unlock the error checking MutEx and we throw the */ +/* exception. Any other return values signifies either an unknown error type */ +/* or that the MutEx has not been properly initialized, which CANNOT happen */ +/* within this class, an Unknown MutEx exception is thrown. */ +/******************************************************************************/ +void Mutex::Unlock() +{ + pthread_mutex_unlock(&m_mutex); + /* + case 0: return; + case EPERM: throw ThreadException(ThreadException::errMutexNotOwned); + default: throw ThreadException(ThreadException::errMutexUnknown); + */ +} + +/******************************************************************************/ +/* bool TryLock() */ +/* */ +/* This method attempts to lock the MutEx and returns true if successful and */ +/* false if the MutEx is already locked. This is done by calling the */ +/* pthread_mutex_trylock() function. If 0 is returned, the MutEx is now */ +/* locked and we can return true, if EBUSY is returned then we cannot lock */ +/* the MutEx and we return false. Any other return values signifies either */ +/* an unknown error type or that the MutEx has not been properly initialized, */ +/* which CANNOT happen within this class so we report a major error and */ +/* terminate the program. */ +/******************************************************************************/ +bool Mutex::TryLock() +{ + return pthread_mutex_trylock(&m_mutex) == 0; + /* + case 0: return true; + case EBUSY: return false; + default: throw ThreadException(ThreadException::errMutexUnknown); + */ +} +#endif //!WIN32 + +//@ Index: src/shared/NetworkNew/SocketManager.cpp =================================================================== --- src/shared/NetworkNew/SocketManager.cpp (revision 0) +++ src/shared/NetworkNew/SocketManager.cpp (revision 0) @@ -0,0 +1,233 @@ +#include "SocketManager.h" +#include "Policies/SingletonImp.h" +#include "Util.h" + +#include "evutil.h" +typedef struct bufferevent* PBUFFEREVENT; + +void +io_handle_write(int fd, short event, void *arg); +void +io_handle_read(int fd, short event, void *arg); + +#ifndef WIN32 + #define WSAECONNRESET 104 +#endif + +INSTANTIATE_SINGLETON_1(SocketManager); +INSTANTIATE_SINGLETON_1(SocketManagerCleaner); + +timeval tv = {0, 50000}; + +void event_log(int severity, const char *msg) +{ + if(!msg) + return; + switch (severity) + { + case _EVENT_LOG_DEBUG: + sLog.outDebug(" %s", msg); + break; + case _EVENT_LOG_MSG: + sLog.outDetail(" %s", msg); + break; + case _EVENT_LOG_WARN: + sLog.outDetail(" %s", msg); + break; + case _EVENT_LOG_ERR: + sLog.outError(" %s", msg); + break; + default: + sLog.outDetail(" %s", msg); + } + return; +} + +void SocketReaderRunnable::run() +{ + if(!sSocketManager.GetEventBase()) + { + sLog.outError(" Couldn't start Socket worker thread. Event_base is wrong!"); + return; + } + + sLog.outBasic(" Socket worker thread is started"); + + while(1) + { + Sleep(10); + if( event_base_loop(sSocketManager.GetEventBase(), 0) == -1 ) + { + sLog.outError(" Critical error is occured in event_loot. Exit."); + sSocketManager.DisconnectAll(); + return; + } + } + sLog.outBasic(" Socket worker thread is finished"); +} + +SocketManager::SocketManager() +{ + m_event_base = (event_base*)event_init(); + if(!m_event_base) + sLog.outError(" FATAL ERROR! Couldn't start libevent!"); + else + { + event_set_log_callback(event_log); + sLog.outBasic(" MaNGOS network uses [%s] event mechanism", event_get_method()); + } + event_base_dispatch(GetEventBase()); +} + +SocketManager::~SocketManager() +{ + event_base_free(GetEventBase()); +} + +bool SocketManager::AddSocket(SocketBase* pSocket) +{ + m_Mutex.Lock(); + m_sockets.insert(pSocket); + m_Mutex.Unlock(); + + + event_set(&pSocket->m_ev_read, (int)pSocket->GetSocket(), EV_READ | EV_PERSIST, io_handle_read, pSocket); + event_base_set(GetEventBase(), &pSocket->m_ev_read); + + if(event_add(&pSocket->m_ev_read, &tv) == -1) + return false; + + event_set(&pSocket->m_ev_write, (int)pSocket->GetSocket(), EV_WRITE, io_handle_write, pSocket); + event_base_set(GetEventBase(), &pSocket->m_ev_write); + + return true; +} + +bool SocketManager::RemoveSocket(SocketBase* pSocket) +{ + //Don't call "delete" for socket! + if(!pSocket) + return false; + m_Mutex.Lock(); + m_sockets.erase(pSocket); + m_Mutex.Unlock(); + sLog.outDebug(" Removed from manager", pSocket->GetSocket()); + event_del(&pSocket->m_ev_read); + event_del(&pSocket->m_ev_write); + return true; +} + +void SocketManager::DisconnectAll() +{ + m_Mutex.Lock(); + SocketSet _tempSocketSet(m_sockets.begin(), m_sockets.end()); + m_Mutex.Unlock(); + for (SocketSet::iterator _it = _tempSocketSet.begin(); _it != _tempSocketSet.end(); ++_it) + { + Socket* pSocket = (Socket*)(*_it); + if(pSocket) + pSocket->Disconnect(); + } + _tempSocketSet.clear(); +} + +#ifdef USE_LEVENT + +void io_handle_read(int fd, short event, void *arg) +{ + if(!arg) + { + sLog.outError(" Bad (null) param in read callback."); + return; + } + + Socket* pSocket = (Socket*)arg; + if(!pSocket->IsConnected()) + return; + + if(event & EV_TIMEOUT) + { + event_add(&pSocket->m_ev_read, &tv); + return; + } + + pSocket->OnReadEvent(); + if(!pSocket->IsConnected()) //Socket can be disconnected after read callback, + return; //so we mast check it + event_add(&pSocket->m_ev_read, &tv); +} + +void +io_handle_write(int fd, short event, void *arg) +{ + if(!arg) + { + sLog.outError(" Bad (null) param in read callback."); + return; + } + + Socket* pSocket = (Socket*)arg; + if(!pSocket->IsConnected()) + return; + + //Lock buffer to send + pSocket->LockWriting(); + int n = send( + pSocket->GetSocket(), + (char*)pSocket->WriteBuffer()->get_buffer(), + pSocket->WriteBuffer()->get_data_size(), 0); + + if(n != SOCKET_ERROR) + { + //for DEBUG only! + if(n == 0) + sLog.outError("DEBUG.Rognar: sent in socket [%u] zero bytes!", pSocket->GetSocket()); + + //Remove sent data from output buffer + pSocket->WriteBuffer()->remove(n); + + if (pSocket->WriteBuffer()->get_data_size()) + {//We still have data. reschedule sending + if(event_add(&pSocket->m_ev_write, NULL) == -1) + { + sLog.outError(" Couldn't reschedule sending.", pSocket->GetSocket()); + pSocket->UnlockWriting(); + pSocket->Disconnect(true); + return; + } + } + } + pSocket->UnlockWriting(); + if (n == -1) + { + if (errno == EAGAIN || errno == EINTR) + { + //"Resource temporarily unavailable" + //This error is returned from operations on nonblocking sockets that cannot be completed immediately, + //for example recv when no data is queued to be read from the socket. + //It is a nonfatal error, and the operation should be retried later. + //It is normal for WSAEWOULDBLOCK to be reported as the result from calling connect + //on a nonblocking SOCK_STREAM socket, since some time must elapse for the connection to be established. + if(event_add(&pSocket->m_ev_write, NULL) == -1) + { + pSocket->Disconnect(true); + return; + } + } + pSocket->Disconnect(true); + return; + } +} + + +void SocketManager::StartSocketThreads() +{ + ZThread::Thread SocketCleaner(new SocketManagerCleaner); + ZThread::Thread tSocketReader(new SocketReaderRunnable); +} + +void SocketManager::StopSocketThreads() +{ + //event_base_loopbreak(GetEventBase()); +} +#endif //USE_LEVENT Index: src/shared/NetworkNew/Mutex.h =================================================================== --- src/shared/NetworkNew/Mutex.h (revision 0) +++ src/shared/NetworkNew/Mutex.h (revision 0) @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __MUTEX_07072007__H__ +#define __MUTEX_07072007__H__ +#include "Common.h" +class Mutex +{ + public: + friend class Condition; + + //Only for Non-Win32/Non-Win64 OS !! + enum MutexType + { fastMutEx, recursiveMutEx, errorcheckMutEx }; + //Description + /******************************************************************************/ + /* class Mutax. */ + /* */ + /* This class encapsulates the basic MutEx functionality into a C++ class. */ + /* The constructor is used to create the MutEx of a certain type, the default */ + /* type being fastMutEx. The destructor will destroy the MutEx. The Lock() */ + /* and Unlock() methods do as expected while TryLock() will lock the MutEx if */ + /* possible but will not block if locking is not possible. Details of these */ + /* member methods are: */ + /* */ + /*----------------------------------------------------------------------------*/ + /* MutEx Type | | | */ + /*--------- | fastMutEx | recursiveMutEx | errorcheckMutEx */ + /* Method | | | | */ + /*----------------------------------------------------------------------------*/ + /* Lock() | Block until MutEx | Block until MutEx | Block until MutEx */ + /* | is free. If MutEx | is free. If Mutex | is free. If Mutex */ + /* | is owned by calling | is owned by calling | is owned by the */ + /* | thread, Deadlock | thread, return true | calling thread, */ + /* | occurs. | and increment count | throw a MutEx */ + /* | | of number of locks. | Would Deadlock */ + /* | | | exception. */ + /*----------------------------------------------------------------------------*/ + /* Unlock() | Unlock the MutEx. | Decrement count of | If the MutEx is */ + /* | | locks, if 0, unlock | currently locked */ + /* | | the MutEx. | by the calling */ + /* | | | thread then */ + /* | | | unlock the MutEx, */ + /* | | | otherwise throw a */ + /* | | | MutEx Not Owned */ + /* | | | exception. */ + /*----------------------------------------------------------------------------*/ + /* TryLock() | Try to lock the | Same functionality | Try to lock the */ + /* | MutEx, return true | as Lock() | MutEx, return true*/ + /* | if successful, or | | if successful, or */ + /* | false if MutEx is | | false if MutEx is */ + /* | already locked. | | already locked. */ + /*----------------------------------------------------------------------------*/ + /* */ + /* NOTE: The Condition Class is a friend of the MutualExclusion class since */ + /* it needs access to the private mutexTheMutex member variable. */ + /******************************************************************************/ + + Mutex(MutexType type = recursiveMutEx); + ~Mutex(); + + void Lock(); + void Unlock(); + bool TryLock(); + + protected: + #ifdef WIN32 + CRITICAL_SECTION m_cs; + #else + pthread_mutex_t m_mutex; + #endif +}; + +//AutoLock _AutoLock(var) +class AutoLock +{ +public: + AutoLock(Mutex& _mutex): mMutex(_mutex) { mMutex.Lock(); } + ~AutoLock() { mMutex.Unlock(); } +private: + Mutex& mMutex; +}; + + +#endif//__MUTEX_07072007__H__ + +//@ Index: src/shared/NetworkNew/Socket.cpp =================================================================== --- src/shared/NetworkNew/Socket.cpp (revision 0) +++ src/shared/NetworkNew/Socket.cpp (revision 0) @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "Socket.h" +#include "SocketManager.h" +#include + +namespace MaNGOS_Network +{ + std::string GetHostIP() + { + std::string sRet= ""; + hostent* pThisHost = gethostbyname(""); + if (!pThisHost) + return sRet; + + char* pIP = inet_ntoa (*(struct in_addr *)*pThisHost->h_addr_list); + if (!pIP) + return sRet; + + sRet = std::string(pIP); + return sRet; + } +}; + +////////////////////////////////////////////////////////////////////////// +//DataBuffer class implementation +DataBuffer::DataBuffer(): +m_pBuffer(NULL), m_bInitialized(false){} + +DataBuffer::~DataBuffer() +{ + if(m_bInitialized) + Destroy(); +} + +void DataBuffer::Destroy() +{ + m_bInitialized = false; + if (m_pBuffer) + { + free(m_pBuffer); + m_pBuffer = NULL; + } +} + +bool DataBuffer::Initialize(IN uint32 unDefSize) +{ + if (m_bInitialized) + return true; + + m_pBuffer = (uint8*)malloc(unDefSize); + m_unDefSize = unDefSize; + m_Offset = 0; + m_bInitialized = (m_pBuffer != NULL); + return m_bInitialized; +} + +bool DataBuffer::append(IN const uint8* pSource, IN uint32 unSize) +{ + if (!m_bInitialized || !pSource) + return false; + + if (unSize == 0) + return true; + + + while (unSize > get_free_size()) + { + // Reallocate + uint8* pTempBuffer = m_pBuffer; // save pointer in case realloc fails + + m_unDefSize += CHUNK*((unSize - get_free_size())/CHUNK) + CHUNK; + + m_pBuffer = (uint8*)realloc(m_pBuffer, m_unDefSize); + if(!m_pBuffer) + { + sLog.outError("Reallocation of socket buffer is failed"); + m_pBuffer = pTempBuffer; + Destroy(); + return false; + } + } + //copies to destination buffer + memcpy(m_pBuffer + m_Offset, (void*)pSource, unSize); + m_Offset += unSize; + return true; +} + +// Retrieves and removes data from the buffer. +void DataBuffer::extract(OUT uint8* pDest, IN uint32 unSize) +{ + if (!m_bInitialized || !pDest || unSize == 0) + { + unSize = 0; + return; + } + + uint32 unBytes = std::min(unSize, m_Offset); + + //copies to destination buffer + memcpy(pDest, (void*)m_pBuffer, unBytes); + // ASSERT(unBytes <= m_Offset); + + //moves the rest to the begin + memmove(m_pBuffer, m_pBuffer + unBytes, m_Offset - unBytes); + + //change offset according to count of copied bytes + m_Offset -= unBytes; +} + +void DataBuffer::peek(OUT uint8* pDest, INOUT uint32 unSize) +{ + if (!m_bInitialized || !pDest || unSize == 0) + { + unSize = 0; + return; + } + + uint32 unBytes = std::min(unSize, m_Offset); + //copies to destination buffer + memcpy(pDest, (void*)m_pBuffer, unBytes); +} + +uint32 DataBuffer::get_data_size() const +{ + return m_Offset; +} + +uint32 DataBuffer::get_free_size() const +{ + return m_unDefSize - m_Offset; +} + +void DataBuffer::remove(IN uint32 unSize) +{ + if (!m_bInitialized || unSize == 0) + return; + + uint32 unBytes = std::min(unSize, m_Offset); + if (unBytes == m_Offset) + { + m_Offset = 0; + return; + } + memmove(m_pBuffer, m_pBuffer + unBytes, m_Offset - unBytes); + m_Offset -= unBytes; +} + +bool DataBuffer::has_data() const +{ + return get_data_size() != 0; +} + +uint8* DataBuffer::get_buffer() +{ + return m_pBuffer; +} + +////////////////////////////////////////////////////////////////////////// +//Socket base class implementation +Socket::Socket(SOCKET hSocket, uint32 unInputBufferSize, uint32 unOutputBufferSize): + SocketBase(hSocket), m_bAbleToSend(true) +{ + WriteBuffer()->Initialize(unOutputBufferSize); + ReadBuffer()->Initialize(unInputBufferSize); + + if (m_SocketHandle == INVALID_SOCKET) + m_SocketHandle = socket(AF_INET, SOCK_STREAM, 0); + + u_long arg = 1; + if(ioctlsocket(GetSocket(), FIONBIO, &arg) == SOCKET_ERROR) + sLog.outError(" Couldn't set nonblocking mode.", GetSocket()); +} + +Socket::~Socket() +{ + sLog.outDebug("Calls Socket::~Socket()\n"); + WriteBuffer()->Destroy(); + ReadBuffer()->Destroy(); + if (m_bConnected) + { + m_bConnected = false; + if(m_SocketHandle != INVALID_SOCKET) + { + ::shutdown(m_SocketHandle, SD_BOTH); + ::closesocket(m_SocketHandle); + } + m_SocketHandle = INVALID_SOCKET; + sSocketManager.RemoveSocket(this); + } +} + +bool Socket::Connect(const char* szAddress, uint32 unPort) +{ + //Function retrieves host information corresponding to a host name from a host database + struct hostent* pHostInfo = ::gethostbyname(szAddress); + if(!pHostInfo) + { + //Log "Could'n connect to [%s:%u], szAddress, unPort" + return false; + } + + m_client.sin_family = pHostInfo->h_addrtype; + m_client.sin_port = ntohs((u_short)unPort); + memcpy(&m_client.sin_addr.s_addr, pHostInfo->h_addr_list[0], pHostInfo->h_length); + + //Function establishes a connection to a specified socket + if(connect(GetSocket(), (const sockaddr*)&m_client, sizeof(m_client)) == SOCKET_ERROR) + { + #ifdef WIN32 + sLog.outError(" Couldn't connect to [%s]. Error [%u]", GetSocket(), GetRemoteAddress().c_str(), ::WSAGetLastError()); + #else + sLog.outError(" Couldn't connect to [%s].", GetSocket(), GetRemoteAddress().c_str()); + #endif//WIN32 + return false; + } + + sLog.outDebug(" Connected to [%s:%u] successfully", (uint32)GetSocket(), szAddress, unPort); + SetOptions(); + m_bConnected = true; + if(!sSocketManager.AddSocket(this)) + return false; + //Notify "child" + OnConnect(); + return true; +} + +//New socket connection accepted +bool Socket::Accept(IN sockaddr_in* psAddress) +{ + if (!psAddress) + return false; + + sLog.outDebug(" New connection accepted.", GetSocket()); + + //Assign client information + memcpy(&m_client, psAddress, sizeof(*psAddress)); + //SetOptions(); + m_bConnected = true; + if(!sSocketManager.AddSocket(this)) + return false; + //Notify "child" + OnConnect(); + return true; +} + +void Socket::SetOptions() +{ + ////////////////////////////////////////////////////////////////////////// + // "Microsoft Press - 1999 - Network Programming For MS Windows" + // + //In order to increase performance and throughput by minimizing overhead, + //the system implements the Nagle algorithm. When an application requests + //to send a chunk of data, the system might hold on to that data for a while + //and wait for other data to accumulate before actually sending it on the wire. + //Of course, if no other data accumulates in a given period of time, the data + //will be sent regardless. This results in more data in a single TCP packet, + //as opposed to smaller chunks of data in multiple TCP packets. + //The overhead is that the TCP header for each packet is 20 bytes long. + //Sending a couple bytes here and there with a 20-byte header is wasteful. + //The other part of this algorithm is the delayed acknowledgments. + //Once a system receives TCP data it must send an ACK to the peer. + //However, the host will wait to see whether it has data it is sending to + //the peer so that it can piggyback the ACK on the data to be sent resulting in one less packet on the network. + //The purpose of this option is to disable the Nagle algorithm, as its behavior can be detrimental in a few cases. + //This algorithm can adversely affect any network application that sends relatively small amounts of data and expects + //a timely response. A classic example is Telnet. Telnet is an interactive application that allows the user + //to log on to a remote machine and send it commands. Typically the user hits only a few keystrokes per second. + //The Nagle algorithm would make such a session seem sluggish and unresponsive. + int arg2 = 1; + if(setsockopt(GetSocket(), IPPROTO_TCP, TCP_NODELAY, (char*)&arg2, sizeof(arg2)) == SOCKET_ERROR) +#ifdef WIN32 + sLog.outError(" Couldn't set option TCP_NODELAY. Error [%u]", GetSocket(), WSAGetLastError()); +#else + sLog.outError(" Couldn't set option TCP_NODELAY", GetSocket()); +#endif//WIN32 +} + +void Socket::Disconnect(bool bAbort) +{ + if(!m_bConnected) + return; + if(bAbort) + sLog.outError(" Abortively disconnected.", GetSocket()); + else + sLog.outDetail(" Gracefully disconnected.", GetSocket()); + m_bConnected = false; + if(m_SocketHandle != INVALID_SOCKET) + { + ////////////////////////////////////////////////////////////////////////// + //shutdown + //To ensure that all data an application sends is received by the peer, + //a well-written application should notify the receiver that no more data is to be sent. + //SD_BOTH disables both sends and receives + ::shutdown(m_SocketHandle, SD_BOTH); + + ////////////////////////////////////////////////////////////////////////// + //closesocket + //The closesocket function closes a socket + //Calling closesocket releases the socket descriptor and any further calls using the socket fail with WSAENOTSOCK. + //If there are no other references to this socket, all resources associated with the descriptor are released. + //This includes discarding any queued data. + ::closesocket(m_SocketHandle); + } + sSocketManager.RemoveSocket(this); + m_SocketHandle = INVALID_SOCKET; + OnDisconnect(); +} + +////////////////////////////////////////////////////////////////////////// +//LibEvent implementation +#ifdef USE_LEVENT + +bool Socket::Send(IN const uint8* pSrc, INOUT uint32 unSize) +{ + if(!m_bConnected) + return false; + + LockWriting(); + WriteBuffer()->append(pSrc, unSize); + int res = event_add(&m_ev_write, NULL); + UnlockWriting(); + if(res == -1) + { + sLog.outError("Socket::Send couldn't add write event"); + } + return (res != -1); +} + +void Socket::OnReadEvent() +{ + if(!m_bConnected) + return; + + //TODO peak MSG_PEEK + LockReading(); + uint32 iosize = recv(GetSocket(), + (char*)(ReadBuffer()->get_buffer() + ReadBuffer()->get_data_size()), + ReadBuffer()->get_free_size(), 0); + + if (iosize == 0 || iosize == -1) + {//If the connection has been gracefully closed, the return value is zero. + //If the connection has been abortively closed, the return value is SOCKET_ERROR. + UnlockReading(); + Disconnect(iosize == -1); + return; + } + + ReadBuffer()->get_offset() += iosize; + UnlockReading(); + OnRead(); +} + +#endif//USE_LEVENT +////////////////////////////////////////////////////////////////////////// + +void Socket::Sendf(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + char slask[5000]; // vsprintf / vsnprintf temporary +#ifdef _WIN32 + vsprintf(slask, format, ap); +#else + vsnprintf(slask, 5000, format, ap); +#endif + va_end(ap); + Send((uint8*)slask, strlen(slask)); +} + + +std::string Socket::GetRemoteAddress() +{ + char* szIP = (char*)inet_ntoa(m_client.sin_addr); + std::string sRemoteAddress = ""; + if (szIP) + sRemoteAddress = std::string(szIP); + return sRemoteAddress; +} + +////////////////////////////////////////////////////////////////////////// +//IOCP implementation +#ifdef USE_IOCP +bool Socket::Send(IN const uint8* pSrc, INOUT uint32 unSize) +{ + if(!unSize) + return false; + + LockWriting(); + if(!WriteBuffer()->append(pSrc, unSize)) + { + UnlockWriting(); + return false; + } + + if (!m_bAbleToSend) + { + UnlockWriting(); + return true; + } + m_bAbleToSend = false; + bool bres = SendBuffer(); + UnlockWriting(); + return bres; +} + +void Socket::SendComplete(uint32 dwIoSize) +{ + if (!dwIoSize) + { + Disconnect(true); + return; + } + + LockWriting(); + WriteBuffer()->remove(dwIoSize); + if (WriteBuffer()->get_data_size()) + SendBuffer(); + else + m_bAbleToSend = true; + UnlockWriting(); +} + +void Socket::ReadComplete(uint32 dwIoSize) +{ + if (dwIoSize > ReadBuffer()->get_free_size()) + { + sLog.outError(" Read buffer is overflowed", GetSocket()); + Disconnect(true); + return; + } + ReadBuffer()->get_offset() += dwIoSize; + OnRead(); +} + +bool Socket::SendBuffer() +{ + if(WriteBuffer()->get_data_size()) + { + ULONG ulFlags = 0; + DWORD dwSendNumBytes = 0; + IOCPBuffer *pOverlapBuff = new IOCPBuffer(); + pOverlapBuff->SetupWrite((char*)WriteBuffer()->get_buffer(), WriteBuffer()->get_data_size()); + int err = WSA_IO_PENDING; + if(::WSASend(GetSocket(), pOverlapBuff->GetWSABuffer(), 1, &dwSendNumBytes, ulFlags, &pOverlapBuff->m_ol, NULL) == SOCKET_ERROR && + WSAGetLastError() != WSA_IO_PENDING + //The error code WSA_IO_PENDING indicates that the overlapped + //operation has been successfully initiated and that completion + //will be indicated at a later time. Any other error code + //indicates that the overlapped operation was not successfully + //initiated and no completion indication will occur. + ) + { + Disconnect(); + delete pOverlapBuff; + pOverlapBuff = NULL; + } + return true; + } + else + { + IOCPBuffer *pOverlapBuff = new IOCPBuffer(IOWriteCompleted); + return TRUE == ::PostQueuedCompletionStatus(GetIOCompletionPort(), 0, (DWORD)this, &pOverlapBuff->m_ol); + } +} +#endif//USE_IOCP +////////////////////////////////////////////////////////////////////////// Index: src/shared/NetworkNew/SocketManager.h =================================================================== --- src/shared/NetworkNew/SocketManager.h (revision 0) +++ src/shared/NetworkNew/SocketManager.h (revision 0) @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __SOCKET_MANAGER_H__ +#define __SOCKET_MANAGER_H__ + +#include "Mutex.h" +#include "Policies/Singleton.h" +#include "Network.h" +#include "../Log.h" + +#define WORKER_THREADS_PER_PROCESSOR 2 +typedef std::set SocketSet; + +class SocketManager : public MaNGOS::Singleton > +{ + public: + SocketManager(); + ~SocketManager(); + + public: + bool AddSocket(SocketBase* pSocket); + bool RemoveSocket(SocketBase* pSocket); + void DisconnectAll(); + + void StartSocketThreads(); + void StopSocketThreads(); + + public: + event_base* GetEventBase() {return m_event_base;} + + private: + struct event_base* m_event_base; + Mutex m_Mutex; + int m_iIOWorkersCount; + SocketSet m_sockets; +}; + +#define sSocketManager MaNGOS::Singleton::Instance() + +class SocketReaderRunnable : public ZThread::Runnable +{ +public: + void run(); +}; + +class SocketWriterRunnable : public ZThread::Runnable +{ +public: + void run(); +}; +//Temporary, but necessary :( +class SocketManagerCleaner : + public MaNGOS::Singleton >, + public ZThread::Runnable +{ + public: + SocketManagerCleaner() + { + m_stopEvent = false; + } + ~SocketManagerCleaner() + { + MapToClean::iterator it; + while (!toClean.empty()) + { + it = toClean.begin(); + delete it->first; + toClean.erase(it); + } + } + + typedef std::map MapToClean; + void Add(SocketBase* pSocket) + { + if(!pSocket) + return; + if(pSocket->IsConnected()) + { + sLog.outError("Abnormal!! Connected socket is marked to delete. Skipped"); + return; + } + + mMutex.Lock(); + time_t _time = time(NULL); + toClean[pSocket] = _time + 5; + mMutex.Unlock(); + } + + void run() // WIN32 + { + while (!m_stopEvent) + { + Sleep(100); + mMutex.Lock(); + time_t _current_time = time(NULL); + MapToClean::iterator it1, it2; + for (it1 = toClean.begin(); it1 != toClean.end();) + { + it2 = it1++; + if (it2->second < _current_time) + { + delete it2->first; + toClean.erase(it2); + } + } + mMutex.Unlock(); + } + toClean.clear(); + } + + void Stop(){ m_stopEvent = true; } + bool m_stopEvent; + Mutex mMutex; + MapToClean toClean; +}; + +#define sAuthSocketManagerCleaner MaNGOS::Singleton::Instance() +#endif //__SOCKET_MANAGER_H__ Index: src/shared/NetworkNew/ListenSocket.h =================================================================== --- src/shared/NetworkNew/ListenSocket.h (revision 0) +++ src/shared/NetworkNew/ListenSocket.h (revision 0) @@ -0,0 +1,114 @@ +#ifndef __LISTER_SOCKET_NWIN32_H__ +#define __LISTER_SOCKET_NWIN32_H__ + +#include "Common.h" +#include "Log.h" + +template +class ListenSocket: + public SocketBase +{ +public: + ListenSocket(const char* szListenAddress, int unPort): + m_bSocketCreated(false) + { + do + { + if((m_SocketHandle = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) + { + sLog.outError(" Couldn't create socket"); + break; + } + + int on = 1; + if (setsockopt(m_SocketHandle, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) == SOCKET_ERROR) + { + sLog.outError(" Couldn't set option SO_REUSEADDR", + m_SocketHandle); + break; + } + + u_long arg = 1; + if(ioctlsocket(m_SocketHandle, FIONBIO, &arg) == SOCKET_ERROR) + { + sLog.outError(" Couldn't set nonblocking mode", + m_SocketHandle); + break; + } + + sockaddr_in address; + if(!strcmp(szListenAddress, "0.0.0.0")) + address.sin_addr.s_addr = htonl(INADDR_ANY); + else + { + hostent* h = gethostbyname(szListenAddress); + if(!h) + { + sLog.outError(" Couldn't retrieve host information corresponding " + "to a host name from a host database", + m_SocketHandle); + break; + } + + memcpy(&address.sin_addr, h->h_addr_list[0], sizeof(in_addr)); + } + + address.sin_family = AF_INET; + address.sin_port = ntohs(unPort); + if (bind(m_SocketHandle, (const sockaddr*)&address, sizeof(sockaddr_in)) == SOCKET_ERROR) + { + sLog.outError(" Couldn't associate a local address with a socket", + m_SocketHandle); + break; + } + + if (listen(m_SocketHandle, 1024) == SOCKET_ERROR) + { + sLog.outError(" Couldn't place a socket in a state in which it" + " is listening for an incoming connection", + m_SocketHandle); + break; + } + m_bSocketCreated = true; + } + while(false); + + if (m_bSocketCreated && sSocketManager.AddSocket(this)) + sLog.outBasic(" Listening started...", m_SocketHandle); + else + { + sLog.outError(" Creating failed", m_SocketHandle); + StopListening(); + } + } + + void Disconnect(bool bAbort = false) { StopListening(); } + bool IsConnected() { return IsListenPortOpened(); } + bool IsListenPortOpened() { return m_bSocketCreated;} + + void OnReadEvent() + { + struct sockaddr_in ClientAddress; + socklen_t addrlen = sizeof(ClientAddress); + SOCKET client_sock; + if ((client_sock = accept(m_SocketHandle, (sockaddr*)&ClientAddress, &addrlen)) == INVALID_SOCKET) + { + sLog.outError(" Couldn't accept connection. Some error. Skipped.", m_SocketHandle); + return; + } + T* pSocket = new T(client_sock); + pSocket->Accept(&ClientAddress); + } + + void StopListening() + { + sLog.outBasic(" Listen socket closed", m_SocketHandle); + sSocketManager.RemoveSocket(this); + ::closesocket(m_SocketHandle); + m_bSocketCreated = false; + } + +private: + bool m_bSocketCreated; +}; +#endif//__LISTER_SOCKET_NWIN32_H__ Index: src/shared/NetworkNew/Socket.h =================================================================== --- src/shared/NetworkNew/Socket.h (revision 0) +++ src/shared/NetworkNew/Socket.h (revision 0) @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2005,2006,2007 MaNGOS + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef __SOCKET_BASE_IMPL_H__ +#define __SOCKET_BASE_IMPL_H__ + +#include "Common.h" +#include "Mutex.h" +#include + +//Returns the local IP Address +namespace MaNGOS_Network +{ + std::string GetHostIP(); +}; + +//Input +#define IN + +//Output +#define OUT + +//In/Out +#define INOUT + +#ifndef note +#define _note_str(x) #x +#define note_str(x) _note_str(x) +#define note(desc) message(__FILE__ "(" note_str(__LINE__) ") : Note: " ##desc) +#endif //note +//How to use it: #pragma note("Message text") + +////////////////////////////////////////////////////////////////////////// +//DataBuffer class declaration +#define CHUNK (16384) +class DataBuffer +{ + public: + //Constructor + DataBuffer(); + + // Destruction. + virtual ~DataBuffer(); + + // Allocates memory. + bool Initialize(IN uint32 unDefSize); + + // Deallocates memory. + void Destroy(); + + // Appends data to the buffer. + bool append(IN const uint8* pSrc, IN uint32 unSize); + + // Retrieves and removes data from the buffer. + void extract(OUT uint8* pDest, IN uint32 unSize); + + // Retrieves data from the buffer, does not remove. + void peek(OUT uint8* pDest, INOUT uint32 unSize); + + // Returns the number of bytes available for extraction. + uint32 get_data_size() const; + + // Returns the number of bytes available for appending. + uint32 get_free_size() const; + + // Removes a number of bytes from the buffer. + void remove(IN uint32 size); + + // Checks whether the buffer has data. + bool has_data() const; + + uint32& get_offset(){return m_Offset;} + + // Retrieves buffer. + uint8* get_buffer(); + + private: + bool m_bInitialized; + uint8* m_pBuffer; + uint32 m_Offset; + uint32 m_unDefSize; +}; + +////////////////////////////////////////////////////////////////////////// +//Socket base class declaration +#define RECVBUF_SIZE (16384) +#define SENDBUF_SIZE (65536) + +class SocketBase +{ +public: + SocketBase(SOCKET hSocket = INVALID_SOCKET) : m_SocketHandle(hSocket), m_bConnected(false), m_pBufEvent(NULL) + {} + ~SocketBase() + { + m_SocketHandle = INVALID_SOCKET; + m_bConnected = false; + m_pBufEvent =NULL; + } + SOCKET GetSocket() { return m_SocketHandle; } + virtual void OnReadEvent() = 0; + virtual bool IsConnected() = 0; + virtual void Disconnect(bool bAbort = false) = 0; + + + SOCKET m_SocketHandle; //Socket handler + bool m_bConnected; //Is socket connected + + struct event m_ev_read; + struct event m_ev_write; + struct bufferevent* m_pBufEvent; +}; + +class Socket : public SocketBase +{ + public: + ////////////////////////////////////////////////////////////////////////// + // Constructor/Destructor + Socket(SOCKET hSocket = INVALID_SOCKET, + uint32 unInputBufferSize = RECVBUF_SIZE, + uint32 unOutputBufferSize = SENDBUF_SIZE); + ~Socket(); + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + // Operations + bool Connect(const char* szAddress, uint32 unPort); + bool Accept(sockaddr_in* address); + void Disconnect(bool bAbort = false); + bool Send(IN const uint8* pSrc, INOUT uint32 unSize); + void Sendf(const char *format, ...); + ////////////////////////////////////////////////////////////////////////// + + //IOCP "Callback" +#ifdef USE_IOCP + void ReadComplete(uint32 dwIoSize); + void SendComplete(uint32 dwIoSize); +#endif//USE_IOCP + + ////////////////////////////////////////////////////////////////////////// + // Virtual notification methods for Socket's "child" + virtual void OnRead() {} + virtual void OnConnect() {} + virtual void OnDisconnect() {} + ////////////////////////////////////////////////////////////////////////// + +#ifdef USE_LEVENT + void OnReadEvent(); + +#endif//USE_LEVENT + + ////////////////////////////////////////////////////////////////////////// + // Socket's buffers + DataBuffer* WriteBuffer() {return &m_WriteBuffer;} + DataBuffer* ReadBuffer() {return &m_ReadBuffer;} + ////////////////////////////////////////////////////////////////////////// + void SetOptions(); + ////////////////////////////////////////////////////////////////////////// + //Win32 specific functional + #ifdef WIN32 + void AssignIOCompletionPort(HANDLE hIOCP) + { + m_IOCompletitionPort = hIOCP; + } + + HANDLE GetIOCompletionPort() + { + return m_IOCompletitionPort; + } + private: + HANDLE m_IOCompletitionPort; + public: + #endif //WIN32 + bool m_bAbleToSend; + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + //Socket's helpers + std::string GetRemoteAddress(); //Retrieves connected client IP address + bool IsConnected() // Retrieves connection status + { + return m_bConnected; + } + + ////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////////// + // Socket mutex helper + void LockWriting() //Locks writing + { + m_WriteMutex.Lock(); + } + void UnlockWriting() //Unlocks writing + { + m_WriteMutex.Unlock(); + } + + void LockReading() //Locks reading + { + m_ReadMutex.Lock(); + } + void UnlockReading() //Unlocks reading + { + m_ReadMutex.Unlock(); + } + ////////////////////////////////////////////////////////////////////////// + + private: + bool SendBuffer(); + + //Mutex for send/recv operation + Mutex m_WriteMutex; + Mutex m_ReadMutex; + + sockaddr_in m_client; //Name of the socket in the sockaddr structure to which the connection should be established. + + ////////////////////////////////////////////////////////////////////////// + //Socket data buffers + DataBuffer m_ReadBuffer; //Input buffer + DataBuffer m_WriteBuffer; //Output buffer +}; +#endif //__SOCKET_BASE_IMPL_H__ Index: src/mangosd/Makefile.am =================================================================== --- src/mangosd/Makefile.am (revision 23) +++ src/mangosd/Makefile.am (working copy) @@ -33,7 +33,7 @@ WorldRunnable.h ## Link world daemon against the shared library -mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../shared/Network/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a +mangos_worldd_LDADD = ../bindings/universal/libmangosscript.la ../game/libmangosgame.a ../shared/Database/libmangosdatabase.a ../shared/Config/libmangosconfig.a ../shared/Auth/libmangosauth.a ../shared/libmangosshared.a ../shared/vmap/libmangosvmaps.a ../shared/NetworkNew/libmangosnetwork.a ../framework/libmangosframework.a ../../dep/src/zthread/libZThread.la ../../dep/src/g3dlite/libg3dlite.a mangos_worldd_LDFLAGS = -L../../dep/src/zthread -L../../dep/src/g3dlite -L../bindings/universal/ $(MYSQL_LIBS) $(LEVENT_LIB) $(POSTGRE_LIBS) -L$(libdir) $(ZLIB) $(COMPATLIB) $(SSLLIB) -export-dynamic ## Additional files to include when running 'make dist' Index: src/mangosd/Master.cpp =================================================================== --- src/mangosd/Master.cpp (revision 23) +++ src/mangosd/Master.cpp (working copy) @@ -136,7 +136,7 @@ uint32 raport = sConfig.GetIntDefault( "Ra.Port", 3443 ); std::string stringip = sConfig.GetStringDefault( "Ra.IP", "0.0.0.0" ); pRAListenSocket = new ListenSocket(stringip.c_str(), raport); - if (!pRAListenSocket->IsListernPortOpened()) + if (!pRAListenSocket->IsListenPortOpened()) sLog.outString("Remote access lister on port %d on %s - FAILED", raport, stringip.c_str()); else sLog.outString("Starting Remote access lister on port %d on %s", raport, stringip.c_str()); @@ -191,7 +191,7 @@ uint32 wsport = sWorld.getConfig(CONFIG_PORT_WORLD); ListenSocket worldListenSocket("0.0.0.0", wsport); - if(worldListenSocket.IsListernPortOpened()) + if(worldListenSocket.IsListenPortOpened()) sSocketManager.StartSocketThreads(); // maximum counter for next ping @@ -200,7 +200,7 @@ uint32 time_to_sleep = 100; ///- Wait for termination signal - while (!World::m_stopEvent && worldListenSocket.IsListernPortOpened()) + while (!World::m_stopEvent && worldListenSocket.IsListenPortOpened()) { #ifdef WIN32 if (m_ServiceStatus == 0) World::m_stopEvent = true; Index: src/game/Makefile.am =================================================================== --- src/game/Makefile.am (revision 23) +++ src/game/Makefile.am (working copy) @@ -256,7 +256,7 @@ GroupRefManager.h ## Link against shared library -libmangosgame_a_LIBADD = ../shared/libmangosshared.a ../shared/Auth/libmangosauth.a ../shared/Config/libmangosconfig.a ../shared/Database/libmangosdatabase.a ../shared/Network/libmangosnetwork.a ../shared/vmap/libmangosvmaps.a +libmangosgame_a_LIBADD = ../shared/libmangosshared.a ../shared/Auth/libmangosauth.a ../shared/Config/libmangosconfig.a ../shared/Database/libmangosdatabase.a ../shared/NetworkNew/libmangosnetwork.a ../shared/vmap/libmangosvmaps.a ## Additional files to include when running 'make dist' # Nothing yet.