• 从零开始编写网络游戏 --- 基础篇


    本篇的内容,会介绍几个内容:单例,dll动态加载以及一些跨平台的处理。

           1、单例:单例模式是一种使用广泛而又比较简单的设计模式,他的定义我就不多介绍了,大家上网一查就知道了,基本都能理解。在游戏开发中,会有很多单件,所以封装一个单例类供后面的开发使用。

           本单例使用模板实现,代码如下:

    1. //singleton.h   
    2. #ifndef _SINGLETON_H   
    3. #define _SINGLETON_H   
    4.   
    5. namespace Blaze  
    6. {  
    7.     template<class T>  
    8.     class Singleton  
    9.     {  
    10.     public:  
    11.         static T* instance()  
    12.         {  
    13.             if (!_instance)  
    14.             {  
    15.                 _instance = new T;  
    16.             }  
    17.             return _instance;  
    18.         }  
    19.   
    20.     protected:  
    21.         /// 使用保护构造是为了用户不能在栈上声明一个实例   
    22.         Singleton() {}  
    23.   
    24.     private:  
    25.         static T* _instance;        /// 实例静态指针   
    26.     };  
    27.   
    28.     /// 静态实例指针初始化   
    29.     template <class T> T* Singleton<T>::_instance = NULL;  
    30.   
    31. // namespace   
    32.   
    33. #endif //_SINGLETON_H  
    //singleton.h
    #ifndef _SINGLETON_H
    #define _SINGLETON_H
    
    namespace Blaze
    {
    	template<class T>
    	class Singleton
    	{
    	public:
    		static T* instance()
    		{
    			if (!_instance)
    			{
    				_instance = new T;
    			}
    			return _instance;
    		}
    
    	protected:
    		/// 使用保护构造是为了用户不能在栈上声明一个实例
    		Singleton() {}
    
    	private:
    		static T* _instance;		/// 实例静态指针
    	};
    
    	/// 静态实例指针初始化
    	template <class T> T* Singleton<T>::_instance = NULL;
    
    } // namespace
    
    #endif //_SINGLETON_H
    
    1. //使用的时候只需要单件类继承此模板即可。   
    2. class Test : public Singleton<Test >{...};            
    //使用的时候只需要单件类继承此模板即可。
    class Test : public Singleton<Test >{...};          

            2、dll(so)动态加载

      在开发网络游戏的过程中,现在已经不是能够单打独斗的年代了,一款游戏基本上不可能有一个人完成,因此分模块开发成为了必然,各自开发相关的模块,然后组合到一起。dll就是分模块开发的产物之一,它的加载有动态和静态之分,各有优势,但是由于服务器程序是需要运行在多个平台,而他们又各自有各自的加载方法,为了方便使用,因此我们队加载dll进行了封装。

            实现使用模板,非常简单,代码如下

    1. // lib_def.h   
    2. #ifndef __LIB_DEF_H_   
    3. #define __LIB_DEF_H_   
    4.   
    5. namespace Blaze  
    6. {  
    7.     // DLL对象创建辅助类   
    8.     template<const TCHAR*& szFileName>  
    9.     class DllApi  
    10.     {  
    11.     public:  
    12.         static BOOL Load()  
    13.         {  
    14.             if(!m_h) m_h = ::LoadLibrary(szFileName);  
    15.             return m_h != NULL;  
    16.         }  
    17.         static void Unload()  
    18.         {  
    19.             if(m_h) ::FreeLibrary(m_h);  
    20.             m_h = NULL;  
    21.         }  
    22.     protected:  
    23.         static HMODULE m_h;  
    24.     };    
    25.     template<const TCHAR*& szFileName> HMODULE DllApi<szFileName>::m_h;  
    26.   
    27.     //库文件前后缀   
    28. #ifdef WIN32   
    29.     #define __DLL_PREFIX    _T("")   
    30.     #define __DLL_SUFFIX    _T(".dll")   
    31. #else   
    32.     #define __DLL_PREFIX    _T("lib")   
    33.     #define __DLL_SUFFIX    _T(".so")   
    34. #endif   
    35.   
    36.     // 声明DLL文件名常量   
    37. #define DECLARE_DLL_FILE(module) extern "C" const TCHAR* module;   
    38.   
    39.     // 定义DLL文件名常量   
    40. #if !defined(_LIB) && !defined(_USE_STATIC_LIB)   
    41.     #define DEFINE_DLL_FILE(module) extern "C" const TCHAR* module = _T("./")""__DLL_PREFIX""_T(#module)""__DLL_SUFFIX;   
    42. #else   
    43.     #define DEFINE_DLL_FILE(module)   
    44. #endif   
    45. }  
    46.   
    47. #endif  

     本例中使用了LoadLibrary,是windows的实现方法,在后面平台相关处理中,我会将linux的函数封装一下,和windows同名。此模板使用方法很简单:

    1. #if defined(_LIB) || defined(_USE_STATIC_LIB)   // 静态库版本   
    2.     #ifndef _LUA_ENGINE_API   
    3.         #define _LUA_ENGINE_API IMPORT_API   
    4.         #pragma comment(lib,  MAKE_LIB_NAME(LuaEngine))   
    5.     #endif   
    6.   
    7.     _LUA_ENGINE_API ILuaEngine* GlobalLuaEngine();  
    8. #else   
    9.     DECLARE_DLL_FILE(LuaEngine);  
    10.     class GlobalLuaEngine : public DllApi<LuaEngine>  
    11.     {  
    12.     typedef ILuaEngine* (*CREATE_PROC)();  
    13.     ILuaEngine* m_p;  
    14.     public:  
    15.         GlobalLuaEngine() : m_p(NULL)  
    16.         {  
    17.             Load();  
    18.             static CREATE_PROC func;  
    19.             if(func == NULL) func = (CREATE_PROC)::GetProcAddress(m_h, "GlobalLuaEngine");  
    20.             if(func != NULL) m_p = func();  
    21.         }  
    22.         operator ILuaEngine* (){ return m_p; }  
    23.         ILuaEngine* operator ->(){ return m_p; }  
    24.     };  
    25. #endif  
    #if defined(_LIB) || defined(_USE_STATIC_LIB)	// 静态库版本
    	#ifndef _LUA_ENGINE_API
    		#define _LUA_ENGINE_API IMPORT_API
    		#pragma comment(lib,  MAKE_LIB_NAME(LuaEngine))
    	#endif
    
    	_LUA_ENGINE_API ILuaEngine* GlobalLuaEngine();
    #else
    	DECLARE_DLL_FILE(LuaEngine);
    	class GlobalLuaEngine : public DllApi<LuaEngine>
    	{
    	typedef ILuaEngine* (*CREATE_PROC)();
    	ILuaEngine* m_p;
    	public:
    		GlobalLuaEngine() : m_p(NULL)
    		{
    			Load();
    			static CREATE_PROC func;
    			if(func == NULL) func = (CREATE_PROC)::GetProcAddress(m_h, "GlobalLuaEngine");
    			if(func != NULL) m_p = func();
    		}
    		operator ILuaEngine* (){ return m_p; }
    		ILuaEngine* operator ->(){ return m_p; }
    	};
    #endif

           如上面代码所示,LuaEngine是一个dll,我们在加载它的时候,使用了一个额外的类,在他的构造函数里面加载了共享库。而且在应用级上也与平台无关。

           3、跨平台的若干处理

           windows的处理相当简单,只是定义一些简单的宏。

    1. // gwindef.h : windows开发定义文件   
    2. #ifndef __G_WIN_DEF_H_   
    3. #define __G_WIN_DEF_H_   
    4.   
    5. #include <windows.h>   
    6. #include <process.h>   
    7. #include <tchar.h>   
    8. #include <unknwn.h>   
    9. #include <stdio.h>   
    10. #include <stdlib.h>   
    11.   
    12. #define SYS_API         WINAPI   
    13. #define STD_CALL        __stdcall   
    14. #if !defined(_LIB)   
    15. #define EXPORT_API      extern "C" _declspec(dllexport)   
    16. #else   
    17. #define EXPORT_API      extern "C"   
    18. #endif   
    19. #if !defined(_LIB) && !defined(_USE_STATIC_LIB)   
    20. #define IMPORT_API      extern "C" _declspec(dllimport)   
    21. #else   
    22. #define IMPORT_API      extern "C"   
    23. #endif   
    24.   
    25. #endif // ndef __G_WIN_DEF_H_  

     而为了开发的时候去除平台无关性,在linux的开发中,我们需要做一些包装,使其在开发过程中和window代码一致。

    1. // glindef.h : linux开发定义文件   
    2. #ifndef __G_LIN_DEF_H_   
    3. #define __G_LIN_DEF_H_   
    4. //    
    5. #include <stdlib.h>   
    6. #include <stdio.h>   
    7. #include <string.h>   
    8. #include <wchar.h>   
    9. #include <unistd.h>   
    10. #include <pthread.h>   
    11. #include <semaphore.h>   
    12. #include <errno.h>   
    13. #include <sys/times.h>   
    14. #include <time.h>   
    15. #include <dlfcn.h>   
    16. #include <sys/types.h>   
    17. #include <linux/unistd.h>   
    18.   
    19. inline _syscall0(pid_t, gettid)        /* Using syscall(2) may be preferable; see intro(2) */  
    20.   
    21. #ifdef UNICODE   
    22. #define _T(str)     L##str   
    23. #else   
    24. #define _T(str)     str   
    25. #endif   
    26.   
    27. #define TRUE        1   
    28. #define FALSE       0   
    29.   
    30. #define MAX_PATH        256   
    31.   
    32. #define SYS_API   
    33. #define STD_CALL   
    34. #define EXPORT_API      extern "C"   
    35. #define IMPORT_API      extern "C"   
    36.   
    37. /// HRESULT 常量定义   
    38. typedef long        HRESULT;  
    39. enum HResult  
    40. {  
    41.     S_OK = ((HRESULT)0x00000000),           /**< 成功,值为0 */  
    42.     S_FALSE = ((HRESULT)0x00000001),            /**< 成功,但值为1 */  
    43.     E_FAIL = _HRESULT_TYPEDEF_(0x80004005),     /**< 未定义错误 */  
    44.     E_NOTIMPL = _HRESULT_TYPEDEF_(0x80004001),      /**< 接口未实现 */  
    45.     E_OUTOFMEMORY = _HRESULT_TYPEDEF_(0x8007000E),  /**< 内存不足 */  
    46.     E_INVALIDARG = _HRESULT_TYPEDEF_(0x80070057),       /**< 无效参数 */  
    47.     E_NOINTERFACE = _HRESULT_TYPEDEF_(0x80004002),  /**< 接口不存在 */  
    48.     E_POINTER = _HRESULT_TYPEDEF_(0x80004003),      /**< 无效指针 */  
    49.     E_HANDLE = _HRESULT_TYPEDEF_(0x80070006),       /**< 无效句柄 */  
    50.     E_ABORT = _HRESULT_TYPEDEF_(0x80004004),        /**< 操作被取消 */  
    51.     E_ACCESSDENIED = _HRESULT_TYPEDEF_(0x80070005), /**< 访问拒绝 */  
    52.     E_PENDING = _HRESULT_TYPEDEF_(0x8000000A),      /**< 操作被挂起 */  
    53.     E_UNEXPECTED = _HRESULT_TYPEDEF_(0x8000FFFF)        /**< 未预料的错误 */  
    54. };  
    55.   
    56. /// 判定 HRESULT 值是否为成功值   
    57. #define SUCCEEDED(Status)   ((HRESULT)(Status) >= 0)   
    58. /// 判定 HRESULT 值是否为失败值   
    59. #define FAILED(Status)      ((HRESULT)(Status) < 0)   
    60.   
    61. /// GUID 类型定义   
    62. /** 
    63. 要定义 GUID 常量请使用 GUID 专门的生成工具(比如 VS 携带的 guidgen.exe 程序)来生成, 
    64. 以确保其唯一性。 
    65. 接口 ID(IID), 类 ID(CLSID)均为 GUID 的别名*/  
    66. struct GUID  
    67. {  
    68.     unsigned long  Data1;  
    69.     unsigned short Data2;  
    70.     unsigned short Data3;  
    71.     unsigned char  Data4[8];  
    72. };  
    73.   
    74. typedef GUID IID;  
    75. typedef GUID CLSID;  
    76. #define REFGUID const GUID&   
    77. #define REFIID const IID&   
    78. #define REFCLSID const CLSID&   
    79.   
    80. /// 判断两个 GUID 是否相等(内联版)   
    81. inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2)  
    82. {  
    83.     return ((long*)&rguid1)[0] == ((long*)&rguid2)[0] &&   
    84.         ((long*)&rguid1)[1] == ((long*)&rguid2)[1] &&  
    85.         ((long*)&rguid1)[2] == ((long*)&rguid2)[2] &&  
    86.         ((long*)&rguid1)[3] == ((long*)&rguid2)[3];  
    87. }  
    88.   
    89. /// 判断两个 GUID 是否相等   
    90. inline BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2)  
    91. {  
    92.     return !memcmp(&rguid1, &rguid2, sizeof(GUID));  
    93. }  
    94.   
    95. #define CopyMemory(dest, src, len)      memcpy((dest), (src),(len))   
    96. #define ZeroMemory(dest, len)       memset((dest), 0, (len))   
    97. #define FillMemory(dest, len, value)        memset((dest), value, (len))   
    98. #define GetCurrentThreadId          gettid   
    99. #define OutputDebugString(str)      tprintf(_T("%s"), str)   
    100. #define LoadLibrary(file)           dlopen(file, RTLD_NOW)   
    101. #define FreeLibrary         dlclose   
    102. #define GetProcAddress          dlsym   
    103.   
    104. inline int GetLastError()  
    105. {  
    106.     return errno;  
    107. }  
    108.   
    109. inline DWORD GetTickCount()  
    110. {  
    111.     static int clkTck = 0;  
    112.     if(clkTck == 0) clkTck = 1000 / ::sysconf(_SC_CLK_TCK);  
    113.     return (DWORD)::times(NULL) * clkTck;   // 不能溢出   
    114. }  
    115.   
    116. inline void Sleep(DWORD ms)  
    117. {  
    118.     struct timespec req, rem;  
    119.     req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000;  
    120.     while(::nanosleep(&req, &rem) && ::GetLastError() == EINTR) req = rem;  
    121. }  
    122.   
    123. inline long InterlockedIncrement(long volatile* v)  
    124. {  
    125.     long src = 1;  
    126.     /* Modern 486+ processor */  
    127.     __asm__ __volatile__(  
    128.         "lock xaddl %0, %1;"  
    129.         :"=r"(src), "=m"(*v)  
    130.         :"0"(src));  
    131.     return src + 1;  
    132. }  
    133.   
    134. inline long InterlockedDecrement(long volatile* v)  
    135. {  
    136.     long src = -1;  
    137.     /* Modern 486+ processor */  
    138.     __asm__ __volatile__(  
    139.         "lock xaddl %0, %1;"  
    140.         :"=r"(src), "=m"(*v)  
    141.         :"0"(src));  
    142.     return src - 1;  
    143. }  
    144.   
    145. #define stricmp                     strcasecmp   
    146.   
    147. #include <ctype.h>   
    148. inline void strupr(char *s)  
    149. {  
    150.     while (*s) {  
    151.         *s = toupper((unsigned char) *s);  
    152.         s++;  
    153.     }  
    154. }  
    155.   
    156. inline void strlwr(char *s)  
    157. {  
    158.     while (*s) {  
    159.         *s = tolower((unsigned char) *s);  
    160.         s++;  
    161.     }  
    162. }  
    163.   
    164. #endif // ndef __G_LIN_DEF_H_  
    // glindef.h : linux开发定义文件
    #ifndef __G_LIN_DEF_H_
    #define __G_LIN_DEF_H_
    // 
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <wchar.h>
    #include <unistd.h>
    #include <pthread.h>
    #include <semaphore.h>
    #include <errno.h>
    #include <sys/times.h>
    #include <time.h>
    #include <dlfcn.h>
    #include <sys/types.h>
    #include <linux/unistd.h>
    
    inline _syscall0(pid_t, gettid)        /* Using syscall(2) may be preferable; see intro(2) */
    
    #ifdef UNICODE
    #define _T(str)		L##str
    #else
    #define _T(str)		str
    #endif
    
    #define TRUE		1
    #define FALSE		0
    
    #define MAX_PATH		256
    
    #define SYS_API
    #define STD_CALL
    #define EXPORT_API		extern "C"
    #define IMPORT_API		extern "C"
    
    /// HRESULT 常量定义
    typedef long		HRESULT;
    enum HResult
    {
    	S_OK = ((HRESULT)0x00000000),			/**< 成功,值为0 */
    	S_FALSE = ((HRESULT)0x00000001),			/**< 成功,但值为1 */
    	E_FAIL = _HRESULT_TYPEDEF_(0x80004005),		/**< 未定义错误 */
    	E_NOTIMPL = _HRESULT_TYPEDEF_(0x80004001),		/**< 接口未实现 */
    	E_OUTOFMEMORY = _HRESULT_TYPEDEF_(0x8007000E),	/**< 内存不足 */
    	E_INVALIDARG = _HRESULT_TYPEDEF_(0x80070057),		/**< 无效参数 */
    	E_NOINTERFACE = _HRESULT_TYPEDEF_(0x80004002),	/**< 接口不存在 */
    	E_POINTER = _HRESULT_TYPEDEF_(0x80004003),		/**< 无效指针 */
    	E_HANDLE = _HRESULT_TYPEDEF_(0x80070006),		/**< 无效句柄 */
    	E_ABORT = _HRESULT_TYPEDEF_(0x80004004),		/**< 操作被取消 */
    	E_ACCESSDENIED = _HRESULT_TYPEDEF_(0x80070005),	/**< 访问拒绝 */
    	E_PENDING = _HRESULT_TYPEDEF_(0x8000000A),		/**< 操作被挂起 */
    	E_UNEXPECTED = _HRESULT_TYPEDEF_(0x8000FFFF)		/**< 未预料的错误 */
    };
    
    /// 判定 HRESULT 值是否为成功值
    #define SUCCEEDED(Status)	((HRESULT)(Status) >= 0)
    /// 判定 HRESULT 值是否为失败值
    #define FAILED(Status)		((HRESULT)(Status) < 0)
    
    /// GUID 类型定义
    /**
    要定义 GUID 常量请使用 GUID 专门的生成工具(比如 VS 携带的 guidgen.exe 程序)来生成,
    以确保其唯一性。
    接口 ID(IID), 类 ID(CLSID)均为 GUID 的别名*/
    struct GUID
    {
        unsigned long  Data1;
        unsigned short Data2;
        unsigned short Data3;
        unsigned char  Data4[8];
    };
    
    typedef GUID IID;
    typedef GUID CLSID;
    #define REFGUID const GUID&
    #define REFIID const IID&
    #define REFCLSID const CLSID&
    
    /// 判断两个 GUID 是否相等(内联版)
    inline BOOL InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2)
    {
    	return ((long*)&rguid1)[0] == ((long*)&rguid2)[0] && 
    		((long*)&rguid1)[1] == ((long*)&rguid2)[1] &&
    		((long*)&rguid1)[2] == ((long*)&rguid2)[2] &&
    		((long*)&rguid1)[3] == ((long*)&rguid2)[3];
    }
    
    /// 判断两个 GUID 是否相等
    inline BOOL IsEqualGUID(REFGUID rguid1, REFGUID rguid2)
    {
        return !memcmp(&rguid1, &rguid2, sizeof(GUID));
    }
    
    #define CopyMemory(dest, src, len)		memcpy((dest), (src),(len))
    #define ZeroMemory(dest, len)		memset((dest), 0, (len))
    #define FillMemory(dest, len, value)		memset((dest), value, (len))
    #define GetCurrentThreadId			gettid
    #define OutputDebugString(str)		tprintf(_T("%s"), str)
    #define LoadLibrary(file)			dlopen(file, RTLD_NOW)
    #define FreeLibrary			dlclose
    #define GetProcAddress			dlsym
    
    inline int GetLastError()
    {
    	return errno;
    }
    
    inline DWORD GetTickCount()
    {
    	static int clkTck = 0;
    	if(clkTck == 0) clkTck = 1000 / ::sysconf(_SC_CLK_TCK);
    	return (DWORD)::times(NULL) * clkTck;	// 不能溢出
    }
    
    inline void Sleep(DWORD ms)
    {
    	struct timespec req, rem;
    	req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000;
    	while(::nanosleep(&req, &rem) && ::GetLastError() == EINTR) req = rem;
    }
    
    inline long InterlockedIncrement(long volatile* v)
    {
    	long src = 1;
    	/* Modern 486+ processor */
    	__asm__ __volatile__(
    		"lock xaddl %0, %1;"
    		:"=r"(src), "=m"(*v)
    		:"0"(src));
    	return src + 1;
    }
    
    inline long InterlockedDecrement(long volatile* v)
    {
    	long src = -1;
    	/* Modern 486+ processor */
    	__asm__ __volatile__(
    		"lock xaddl %0, %1;"
    		:"=r"(src), "=m"(*v)
    		:"0"(src));
    	return src - 1;
    }
    
    #define stricmp						strcasecmp
    
    #include <ctype.h>
    inline void strupr(char *s)
    {
    	while (*s) {
    		*s = toupper((unsigned char) *s);
    		s++;
    	}
    }
    
    inline void strlwr(char *s)
    {
    	while (*s) {
    		*s = tolower((unsigned char) *s);
    		s++;
    	}
    }
    
    #endif // ndef __G_LIN_DEF_H_

           代码都比较简单,我也不对这些做详细的解析,功能就是对一些常用函数改装成windows相关函数的名字。如果在开发中遇到了其他的情况,也可以加到此文件中,以方便应用开发。

  • 相关阅读:
    Linux常用命令
    cookie机制、session机制
    http状态码、错误分析
    安装“Microsoft SQL Server 2014 Management Objects”时报错"Error Writing to file: Microsoft.SqlServer.XEvent.Linq.dll."
    Installshield build all installer in development computer
    C++使用RabbitMQ类库做客户端与RabbitMQ Server通讯,生成C++可调用的rabbimq.*.dll的过程
    由hibernate配置cascade而导致的软件错误,并分析解决此问题的过程
    由hibernate配置inverse="true"而导致的软件错误,并分析解决此问题的过程
    JVM原理解析
    Add lombok to IntelliJ IDEA
  • 原文地址:https://www.cnblogs.com/zhaoshuireshui/p/3653284.html
Copyright © 2020-2023  润新知