Windows平台网络游戏服务器端开发

传奇源码分析-服务器端(LoginSvr服务器分析)    LoginSvr服务器g_gcSock Local    5500端口1.首先从LoginSvr.cpp  WinMain分析:   1) CheckAvailableIOCP : 检查是不是NT,2000的系统(IOCP)   2) InitInstance: 初始化界面,加载WSAStartup       GetDBManager()->Init( Inse 阅读详情
当今网络游戏在中国大陆已经在大范围的蔓延,暂且不论这样的一种趋势会带来什么样的游戏产业趋势。这里只就网络游戏的制作和大家进行交流,同时将自己的制作经验写处理,希望为中国的游戏业的发展做出一点点的贡献。。


网络游戏的程序开发从某种意义上来看,最重要的应该在于游戏服务器端的设计和制作。对于服务器端的制作。将分为以下几个模块进行:


1.网络通信模块
2.协议模块
3.线程池模块
4.内存管理模块
5.游戏规则处理模块
6.后台游戏仿真世界模块。


现在就网络中的通信模块处理谈一下自己的看法!!


在网络游戏客户端和服务器端进行交互的双向I/O模型中分别有以下几种模型:
1. Select模型
2. 事件驱动模型
3. 消息驱动模型
4. 重叠模型
5. 完成端口重叠模型。


  在这样的几种模型中,能够通过硬件性能的提高而提高软件性能,并且能够同时处理成千上百个I/O请求的模型。服务器端应该采用的最佳模型是:完成端口模型。然而在众多的模型之中完成端口的处理是最复杂的,而它的复杂之处就在于多服务器工作线程并行处理客户端的I/O请求和理解完成端口的请求处理过程。


对于服务器端完成端口的处理过程总结以下一些步骤:


1. 建立服务器端SOCKET套接字描述符,这一点比较简单。
例如:
SOCKET server_socket;
Server_socket = socket(AF_INET,SOCK_STREAM,0);


2.绑定套接字server_socket。
Const int SERV_TCP_PORT = 5555;
struct sockaddr_in server_address.


memset(&server_address, 0, sizeof(struct sockaddr_in));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(SERV_TCP_PORT);
//绑定
Bind(serve_socket,( struct sockaddr *)&server_address, sizeof(server_address));


2. 对于建立的服务器套接字描述符侦听。
Listen(server_socket ,5);


3. 初始化我们的完成端口,开始的时候是产生一个新的完成端口。
HANDLE hCompletionPort;
HCompletionPort = CreateIoCompletionPort(NULL,NULL,NULL,0);


4. 在我们已经产生出来新的完成端口之后,我们就需要进行系统的侦测来得到系统的硬件信息。从而来定出我们的服务器完成端口工作线程的数量。


SYSTEM_INFO system_info;
GetSystemInfo(&system_info);


  在我们知道我们系统的信息之后,我们就需要做这样的一个决定,那就是我们的服务器系统该有多少个线程进行工作,我一般会选择当前处理器的2倍来生成我们的工作线程数量(原因考虑线程的阻塞,所以就必须有后备的线程来占有处理器进行运行,这样就可以充分的提高处理器的利用率)。


代码:
WORD threadNum = system_info. DwNumberOfProcessors*2+2;
for(int i=0;I<threadNum;i++)
{
    HANDLE hThread;
    DWORD dwthreadId;
    hThread = _beginthreadex(NULL,ServerWorkThrea,  (LPVOID)hCompletePort,0,&dwthreadId);
    CloseHandle(hThread); 
}
CloseHandle(hThread)在程序代码中的作用是在工作线程在结束后,能够自动销毁对象作用。


6. 产生服务器检测客户端连接并且处理线程。
HANDLE hAcceptThread;
DWORD dwThreadId;
hAcceptThread= _beginthreadex(NULL,AcceptWorkThread,NULL, &dwThreadId);
CloseHandle(hAcceptThread);


 


7.连接处理线程的处理,在线程处理之前我们必须定义一些属于自己的数据结构体来进行网络I/O交互过程中的数据记录和保存。


首先我要将如下几个函数来向大家进行解析:
1.
HANDLE CreateIoCompletionPort (
    HANDLE FileHandle, // handle to file
    HANDLE ExistingCompletionPort, // handle to I/O completion port
    ULONG_PTR CompletionKey, // completion key
    DWORD NumberOfConcurrentThreads // number of threads to execute concurrently
);
参数1:
可以用来和完成端口联系的各种句柄,在这其中可以包括如下一些:
套接字,文件等。


参数2:
已经存在的完成端口的句柄,也就是在第三步我们初始化的完成端口的句柄就可以了。


参数3:
这个参数对于我们来说将非常有用途。这就要具体看设计者的想法了, ULONG_PTR对于完成端口而言是一个单句柄数据,同时也是它的完成键值。同时我们在进行
这样的GetQueuedCompletionStatus(….)(以下解释)函数时我们可以完全得到我们在此联系函数中的完成键,简单的说也就是我们在CreateIoCompletionPort(…..)申请的内存块,在GetQueuedCompletionStatus(……)中可以完封不动的得到这个内存块,并且使用它。这样就给我们带来了一个便利。也就是我们可以定义任意数据结构来存储我们的信息。在使用的时候只要进行强制转化就可以了。


参数4:
引用MSDN上的解释
[in] Maximum number of threads that the operating system allows to concurrently process I/O completion packets for the I/O completion port. If this parameter is zero, the system allows as many concurrently running threads as there are processors in the system.
这个参数我们在使用中只需要将它初始化为0就可以了。上面的意思我想大家应该也是了解的了!嘿嘿!!




我要向大家介绍的第二个函数也就是
2.
BOOL GetQueuedCompletionStatus(
    HANDLE CompletionPort, // handle to completion port
    LPDWORD lpNumberOfBytes, // bytes transferred
    PULONG_PTR lpCompletionKey, // file completion key
    LPOVERLAPPED *lpOverlapped, // buffer
    DWORD dwMilliseconds // optional timeout value
);
参数1:
我们已经在前面产生的完成端口句柄,同时它对于客户端而言,也是和客户端SOCKET连接的那个端口。


参数2:
一次完成请求被交换的字节数。(重叠请求以下解释)


参数3:
完成端口的单句柄数据指针,这个指针将可以得到我们在CreateIoCompletionPort(………)中申请那片内存。
借用MSDN的解释:
[out] Pointer to a variable that receives the completion key value associated with the file handle whose I/O operation has completed. A completion key is a per-file key that is specified in a call to CreateIoCompletionPort.
所以在使用这个函数的时候只需要将此处填一相应数据结构的空指针就可以了。上面的解释只有大家自己摆平了。


参数4:
重叠I/O请求结构,这个结构同样是指向我们在重叠请求时所申请的内存块,同时和lpCompletionKey,一样我们也可以利用这个内存块来存储我们要保存的任意数据。以便于我们来进行适当的服务器程序开发。
[out] Pointer to a variable that receives the address of the OVERLAPPED structure that was specified when the completed I/O operation was started.(MSDN)


3.
int WSARecv(
    SOCKET s, 
    LPWSABUF lpBuffers, 
    DWORD dwBufferCount, 
    LPDWORD lpNumberOfBytesRecvd, 
    LPDWORD lpFlags, 
    LPWSAOVERLAPPED lpOverlapped, 
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine 
);
这个函数也就是我们在进行完成端口请求时所使用的请求接受函数,同样这个函数可以用ReadFile(………)来代替,但不建议使用这个函数。


参数1:
已经和Listen套接字建立连接的客户端的套接字。


参数2:
用于接受请求数据的缓冲区。
[in/out] Pointer to an array of WSABUF structures. Each WSABUF structure contains a pointer to a buffer and the length of the buffer.(MSDN)。
参数3:
参数2所指向的WSABUF结构的数量。
[in] Number of WSABUF structures in the lpBuffers array.(MSDN)


参数4:


[out] Pointer to the number of bytes received by this call if the receive operation completes immediately. (MSDN)


参数5:
[in/out] Pointer to flags.(MSDN)
参数6:


这个参数对于我们来说是比较有作用的,当它不为空的时候我们就是提出我们的重叠请求。同时我们申请的这样的一块内存块可以在完成请求后直接得到,因此我们同样可以通过它来为我们保存客户端和服务器的I/O信息。
参数7:
[in] Pointer to the completion routine called when the receive operation has been completed (ignored for nonoverlapped sockets).(MSDN)
4.
int WSASend(
    SOCKET s, 
    LPWSABUF lpBuffers, 
    DWORD dwBufferCount, 
    LPDWORD lpNumberOfBytesSent, 
    DWORD dwFlags, 
    LPWSAOVERLAPPED lpOverlapped, 
    LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine 
);
参数解释可以参考上面或者MSDN。在这里就不再多说了。


下面就关client端用户连接(connect(……..))请求的处理方式进行


举例如下:
const int BUFFER_SIZE = 1024;
typedef struct IO_CS_DATA
{
    SOCKET clisnt_s; //客户端SOCKET
    WSABUF wsaBuf; 
    Char inBuffer[BUFFET_SIZE];
    Char outBuffer[BUFFER_SIZE];
    Int recvLen;
    Int sendLen;
    SYSTEM_TIME start_time;
    SYSTEM_TIME start_time;
}IO_CS_DATA;




UINT WINAPI ServerAcceptThread(LPVOID param)
{
    SOCKET client_s;
    HANDLE hCompltPort = (HANDLE) param;
    struct sockaddr_in client_addr;
    int addr_Len = sizeof(client_addr);
    LPHANDLE_DATA hand_Data = NULL; 
    while(true)
    {
        If((client_s=accept(server_socket,NULL,NULL)) == SOCKET_ERROR)
        {
            printf("Accept() Error: %d",GetLastError());
            return 0;
        }
        hand_Data = (LPHANDLE_DATA)malloc(sizeof(HANDLE_DATA));
        hand_Data->socket = client_s;
        if(CreateIoCompletionPort((HANDLE)client_s,hCompltPort,(DWORD)hand_Data,0)==NULL)
        {
            printf("CreateIoCompletionPort()Error: %d", GetLastError());
        }
        else
        { 
            game_Server->RecvDataRequest(client_s);
        }
    }
    return 0;
}


在这个例子中,我们要阐述的是使用我们已经产生的接受连接线程来完成我们响应Client端的connect请求。关于这个线程我们同样可以用我们线程池的方式来进行生成多个线程来进行处理,其他具体的函数解释已经在上面解释过了,希望不懂的自己琢磨。
关于game_Sever object的定义处理将在下面进行介绍。


class CServerSocket : public CBaseSocket
{
public:
    CServerSocket();
    virtual ~CServerSocket();
    bool StartUpServer(); //启动服务器
    void StopServer(); //关闭服务器
    //发送或者接受数据(重叠请求)
    bool RecvDataRequest(SOCKET client_s); 
    bool SendDataRequest(SOCKET client_s,char *buf,int b_len);


    void ControlRecvData(SOCKET client_s,char *buf,int b_len);


    void CloseClient(SOCKET client_s); 
private:
    friend UINT WINAPI GameServerThread(LPVOID completionPortID); //游戏服务器通信工作线程
private:
    void Init();
    void Release();
    bool InitComplePort();
    bool InitServer();
    bool CheckOsVersion();
    bool StartupWorkThread();
    bool StartupAcceptThread();
private:
    enum { SERVER_PORT = 10006};
    UINT cpu_Num; //处理器数量
    CEvent g_ServerStop; //服务器停止事件
    CEvent g_ServerWatch; //服务器监视事件
public:
    HANDLE hCompletionPort; //完成端口句柄
};


在上面的类中,是我们用来处理客户端用户请求的服务器端socket模型。
网络游戏制作技术(二)—— 消息打包处理部分
   
  
   续上在上面我简单的说了一下服务器完成端口处理部分,接下来我想大家介绍一下关于如何建立服务器和客户端的联系规则,也就是服务器和客户端的游戏协议部分。有不足之处希望大家和我进行交流。


  首先解释一下这里协议的概念,协议大家都了解是一种通信规则,例如:TCP/IP,UDP等等,这些是我们在网络通信过程中所处理使用的协议。而我们这里的协议是我们的游戏服务器和客户端的通信规则。简而言之,也就是客户端发送到服务器的数据包和服务器发送的数据包双方解释规则。下面就通过几个部分来具体介绍这种协议的建立和处理。


消息头定义


  如果我们能够解释双方的数据包的意义,我们就必须为双方数据包定义一个统一规则的消息头,我是这么定义消息头的。服务器数据包和客户端数据包分别定义不同的消息头。以下就是双方消息头的简单定义。


struct ServerMsg_Head //服务器消息头
{
    WORD s_version; //版本信息
    BYTE s_flages; //消息标志
    BYTE s_who; //消息驱动者
    BYTE s_sort; //消息类别
    BYTE s_value; //消息值
    WORD s_len; //消息长度
} ;


struct ClientMsg_Head //客户端消息头
{
    WORD c_version; //版本信息
    WORD c_flages //消息标志
    WORD c_sort; //消息类别
    WORD c_value; //消息值
    WORD c_scene; //场景信息
    WORD c_len; //消息长度
};


以上是我个人简单定义的消息头,具体的各个参数意义,就是需要规划设计的人来定了。这些我就不多说了。


  在我们处理完我们的消息头后,我们就可以将我们的具体游戏数据进行打包。关于数据打包,我们必须要处理两件事情:数据打包,数据加密。为此我就建立相应的class来处理这样的一些操作。DataCtrl.h处理如下:


class Ppackage类可以拆解为两个单独处理类,打包类和解包类。而此处我就用下面一个类来进行处理。只是给大家开个头,要设计的更好还是靠大家共同来进行斟酌呀!!




class PPackage //游戏数据包处理类
{
public:
    PPackage(BYTE msg_type); //设置所打包消息类型
    virtual ~PPackage();
    //消息数据打包部分
    void SetMsgHead(void *); //设置消息头
    void AddByte(BYTE data); //加入一字节
    void AddWord(WORD data); //加入二字节
    void AddDword(DWORD data); //加入四字节
    void AddPoint(POINT data); //加入八字节
    void AddBuf(char * data ,int data_len); //加入多个字节
    //消息内容获取
    void FinishPack(); //完成打包
    char *GetPackage(); //获取数据包
    int GetPacketLen(); //获取数据包长度




    //消息数据解包部分
    void SetMsgPackage(char *buf,int _Len); //将获取消息进行录入
    void *GetMsgHead(); //获取消息头数据 
    BYTE GetByte(); //获取一字节
    WORD GetWord(); //获取二字节
    DWORD GetDword(); //获取三字节
    POINT * GetPoint(); //获取四字节
    char * GetBuf(int buf_len); //获取多字节
    bool IfFinishGet(); //是否完成解包


private:


    void Init();
    void Release();
    void StartBindPakage(); //开始打包
    void StartUndoPackage(); //开始解包
    bool MessageEncrypt(); //消息加密
    bool MessageUndo(); //消息解密


private:


private:
    BYTE msg_type; / /{1-SERVER_PACKAGE=1,2-CLIENT_PACKAGE=2}
    char * msg_buffer; 
    char * buffer; //后备缓冲区
    int msg_len;
    //消息内容长度
    Server_Msg_Head msg_Head; //消息头
    int buf_Len;
    int current_pos; //指针的当前位置
protected:
};


  以上就是关于服务器和消息打包类的一些建立和解释,这些方面知识其实也没有什么,主要是“仁者见仁,智者见智”了。而对于网络游戏的制作最重要的还是在于Game World的规划和设计,同时这个方面也是最难和最不好处理的。随后将和大家进行探讨。。 
网络游戏制作技术(三)—— 线程池处理部分
   
  
   续上在这里我将要向大家简单介绍一下游戏服务器中必须要处理另外一项主要技术:


线程池技术


  开始 我来向大家简单来介绍一下线程池的概念,先简单了解下线程先,线程可以理解为一个function , 是一个为了进行某一项任务或者处理某一项具体事务的函数。例如:


UINT WINAPI FunctionCtrl(void *) //线程处理函数
{
    进行某一项任务或者处理某一项具体事务
    ………….
    return EXITFUNCTION_CODE; //退出码
}


  而我们的线程池自身可以理解为是很多线程的一个管理者也可以说是一个很多线程的统筹者。因为我们的线程池具有生成线程功能也具有撤消线程的权利。这就是简单的线程池的概念(我的理解,呵呵!!)接下来就来具体介绍线程池了!!


  首先 介绍我们为什么要使用线程池技术呢?大家都知道我们的游戏服务器端要处理大量的用户请求,,同时需要发送大量的游戏数据到客户端,从而来驱动客户端程序的执行和维持游戏的进行。那我们的服务器端是如何进行处理的呢?其实在这里我们就充分用到了线程池技术。
那么用这种技术有什么好处和优点呢?以下就来简述这些,有不足之处和不当之处希望有心人指正,呵呵!!


  大家都了解在我们服务器整个运行过程中,我们将整个运行时间分成很多个时间片。而对于这些已经分成的各个微小的时间片而言,在各个不同时间片中要处理的用户请求和需要发送到用户端的游戏数据量也将是不一样的。而处理用户的请求和发送数据到客户端的工作都是由一系列的线程来执行的。




鉴于上面,这样我们就可以感性的设想下服务器运行中的两种情况:
  第一种在我们服务器运行到某个时间片需要处理大量的用户请求和发送大量数据,有这样繁重的工作任务,我们就需要有很多的工作者线程来处理完成这样的任务,以此来满足我们的工作需要。这样说我们就必须拥有很多工作者线程。


  第二种在我们服务器运行到某个时间片需要处理的用户请求和发送数据工作量比较小,由于任务比较少,我们用来处理任务的工作者线程也就不需要很多。也就是说我们只要有少量的工作者线程就可以达到我们的工作要求了。


  对于上面的两种情况,我们可以说明这样的一个事实,也就是说我们服务器在运行过程中运行状态是动态改变的,呼忙呼闲,时急时慢的。服务器的这样的行为动作和性质可以做一个如下比喻:服务器就是一个企业,在企业业务非常忙的时候,公司的员工数量就必须要增多来满足业务的需要。而在企业不景气的时候,接的业务也就比较少,那么来说就会有很多员工比较闲。那我们该怎么办呢?为了不浪费公司资源和员工自身资源,我们就必须要裁减员工,从而来配合公司的运行。而做这样工作的可能是公司的人力资源部或者其他部分。现在就认为是人力资源部了。呵呵。


  对于上面的比喻我们来抓几个关键词和列举关键词和我们主题对象进行对照,以此来帮大家来简单理解服务器运行和线程池。


企业 : 游戏服务器
人力资源部 : 线程池
职员 : 工作者线程


在说了这么多的废话后,就具体的将线程池模型 ThreadPool.h文件提供以供大家参考:


class GThreadPoolModel
{
    friend static UINT WINAPI PoolManagerProc(void* pThread); //线程池管理线程
    friend static UINT WINAPI WorkerProc (void* pThread); //工作者线程
    enum SThreadStatus //线程池状态
    {
        BUSY,
        NORMAL,
        IDLE
    };
    enum SReturnvalue //线程返回值
    {
        MANAGERPROC_RETURN_value = 10001,
        WORKERPROC_RETURN_value = 10002,
        …………….
    };
public:
    GThreadPoolModel ();
    virtual ~ GThreadPoolModel ();
    virtual bool StartUp(WORD static_num,WORD max_num)=0; //启动线程驰
    virtual bool Stop(void )=0; //停止线程池
    virtual bool ProcessJob(void *)=0; //提出工作处理要求
protected:
    virtual bool AddNewThread(void )=0; //增加新线程
    virtual bool DeleteIdleThread(void)=0; //删除空闲线程
    static UINT WINAPI PoolManagerProc (void* pThread); //线程池管理线程
    static UINT WINAPI WorkerProc (void* pThread); //工作者线程
    GThreadPoolModel::SThreadStatus GetThreadPoolStatus( void ); //获取线程池当前工作状态
private:
    void Init();
    void Release();
protected:
    ………………………..
private:
};


以上是线程池模型的一个简单class,而对于具体的工作处理线程池,可以由此模型进行继承。以此来满足具体的需要。到这里就简单的向大家介绍了线程池的处理方式。有不对之处望指正。同时欢迎大家和我交流。
 
网络游戏制作技术(四)—— 服务器内存管理部分
   
  
   续上在这里我将要向大家简单介绍一下游戏服务器中必须要处理另外一项主要技术:
  内存分配处理技术也可以称为内存池处理技术(这个比较洋气,前面通俗的好,呵呵)


  开始向大家介绍一般情况下我们对于内存的一些基本操作。简单而言,内存操作就只有三个步骤:申请、使用、销毁。而对于这些操作我们在C和C++中的处理方式略有不同:


在C中我们一般用malloc(….)函数来进行申请,而对应销毁已经申请的内存使用free(…)函数。
在C++我们一般使用new操作符和delete操作符进行处理申请和销毁。
大家一定要问了,我们一般都是这样处理的呀!!没有什么可以说的哦!!呵呵,我感觉就有还是有一些东东和大家聊的哦。先聊简单几条吧!!


1.Malloc(…..)和free(….), new ….和 delete …必须成对出现不可以混杂哦,混杂的话,后果就不可以想了哦!!(也没有什么,就是内存被泄漏了,呵呵)


2.在我们使用new …和delete ….一定要注意一些细节,否则后果同上哦!!什么细节呢?下面看一个简单的例子:
char *block_memory = NULL;
block_memory = new char[1024];
delete block_memory;
block_memory = NULL;
大家沉思一会。。。。。。。。。
大家看有错吗?没有错吧!!


  如果说没有错的,就要好好补补课了,是有错的,上面实际申请的内存是没有完全被释放的,为什么呢?因为大家没有注意第一条的完全匹配原则哦,在new 的时候有[ ],我们在delete 怎么就没有看见[ ] 的影子呢? 这就造成了大错有1023个字节没有被释放。正确的是 : delete []block_memory;


  关于内存基本操作的我是说这两条,其他要注意还是有的,基本就源于此了。


  了解了上面那些接下来就想大家说说服务器内存处理技术了。上面都没有弄清楚了,就算了。呵呵。


  大家都知道,我们的服务器要频繁的响应客户端的消息同时要将消息发送到客户端,并且还要处理服务器后台游戏World的运行。这样我们就必须要大量的使用内存,并且要进行大量的内存操作(申请和销毁)。而在这样的操作中,我们还必须要保证我们的绝对正确无误,否则就会造成内存的泄漏,而内存泄漏对于服务器而言是非常可怕的,也可能就是我们服务器设计失败的毒药。而我们如何进行服务器内存的正确和合理的管理呢?那就是我们
必须建立一套适合我们自己的内存管理技术。现在就向大家说一说我在内存管理方面的一些做法。
基本原理先用图形表示一下:


 


  上面的意思是:我们在服务器启动过程中就为自己申请一块比较大的内存块,而我们在服务器运行过程中需要使用内存我们就到这样一块比较大已经申请好的内存块中去取。而使用完后要进行回收。原理就是这么简单。而最重要的是我们如何管理这个大的内存块呢?
(非常复杂也比较难,呵呵)


首先 就内存块操作而言就只有申请(类似 new)和回收(类似 delete)。
其次 我们必须要清楚那些内存我们在使用中,那些是可以申请的。


关于上面我简单将这样的一些数据结构和class定义在下面供大家参考使用。


typedef struct MemoryBlock //内存块结构
{
    void *buffer; //内存块指针
    int b_Size; //内存块尺寸
} MemoryBlock;


class CMemoryList //列表对象类(相当于数组管理类)
{
public:
    CMemoryList();
    virtual ~ CMemoryList();
    void InitList(int data_size,int data_num);//初始化列表数据结构尺寸和数量
    void AddToList(void *data); //加入列表中
    void DeleteItem(int index); //删除指定索引元素
    ……………..
private:
    void Init();
    void Release();
private:
    void *memory;
    int total_size;
    int total_num;
protected:
};


classs CMemoryPool //内存池处理类
{
public:
    CMemoryPool(); 
    virtual ~ CMemoryPool();
    bool InitMemoryPool(int size); //初始化内存池
    void * ApplicationMemory(int size); //申请指定size内存
    void CallBackMemory(void *,int size); //回收指定size内存


private:
    void Init();
    void Release():
    MemoryBlock *UniteMemory(MemoryBlock *block_a,MemoryBlock * block_b); //合并内存




private:
    MemoryBlock memoryPool_Block; //内存池块
    CMemoryList *callBackMemory_List; //回收内存列表
    CMemoryList *usingMemory_List; //使用中内存列表
    CMemoryList *spacingMemory_List; //空白内存列表
protected:
};


以上就是这个内存管理类的一些基本操作和数据定义,class CMemoryList 在这里不是重点暂且就不说了,有空再聊。而具体的内存池处理方法简单叙述如下:


函数InitMemoryPool(): 初始化申请一块超大内存。
函数ApplicationMemory():申请指定尺寸,申请内存成功后,要将成功申请的内存及其尺寸标示到usingMemory_List列表,同时要将spacingMemory_List列表进行重新分配。以便于正确管理。
函数CallBackMemory():回收指定尺寸内存,成功回收后,要修改spacingMemory_List列表,同时如果有相邻的内存块就要合并成一个大的内存块。usingMemory_List修改使用列表,要在使用列表中的这一项删除。


  以上就是一些简单处理说明,更加详细的就需要大家自己琢磨和处理了。我就不细说了。呵呵。不足之处就请大家进行指正,以便让我们大家都提高。先谢谢了。


 
网络游戏制作技术(五)—— 线程同步和服务器数据保护
   
  
  最近因为自己主持的项目出现些问题,太忙了,所以好久都没有继续写东西和大家进行探讨制作开发部分了。在这一节中就要向大家介绍另外一个重要的部分,并且也是最头疼的部分:线程同步和数据保护。


  关于线程的概念我在前面的章节中已经介绍过了,也就在这里不累赘—“重复再重复”了。有一定线程基础的人都知道,线程只要创建后就如同脱缰的野马,对于这样的一匹野马我们怎么来进行控制和处理呢?简单的说,我们没有办法进行控制。因为我们更本就没有办法知道CPU什么时候来执行他们,执行他们的次序又是什么?


  有人要问没有办法控制那我们如何是好呢?这个问题也正是我这里要向大家进行解释和说明的,虽然我们不能够控制他们的运行,但我们可以做一些手脚来达到我们自己的意志。


  这里我们的做手脚也就是对线程进行同步,关于同步的概念大家在《操作系统》中应该都看过吧!不了解的话,我简单说说:读和写的关系(我读书的时候,请你不要在书上乱写,否则我就没有办法继续阅读了。)


处理有两种:用户方式和内核方式。
用户方式的线程同步由于有好几种:原子访问,关键代码段等。


  在这里主要向大家介绍关键代码段的处理(我个人用的比较多,简单实用)。先介绍一下它的一些函数,随后提供关键代码段的处理类供大家参考(比较小,我就直接贴上来了)


VOID InitializeCriticalSection( //初始化互斥体
    LPCRITICAL_SECTION lpCriticalSection // critical section
);


VOID DeleteCriticalSection( //清除互斥体
    LPCRITICAL_SECTION lpCriticalSection // critical section
);


VOID EnterCriticalSection( //进入等待
    LPCRITICAL_SECTION lpCriticalSection // critical section
);


VOID LeaveCriticalSection( //释放离开
    LPCRITICAL_SECTION lpCriticalSection // critical section
);


以上就是关于关键代码段的基本API了。介绍就不必了(MSDN)。而我的处理类只是将这几个函数进行了组织,也就是让大家能够更加理解关键代码端


.h
class CCriticalSection //共享变量区类
{
public:
    CCriticalSection();
    virtual ~CCriticalSection();
    void Enter(); //进入互斥体
    void Leave(); //离开互斥体释放资源
private:
    CRITICAL_SECTION g_CritSect;
};


.cpp
CCriticalSection::CCriticalSection()
{
    InitializeCriticalSection(&g_CritSect);
}


CCriticalSection::~CCriticalSection()
{
    DeleteCriticalSection(&g_CritSect);
}


void CCriticalSection::Enter()
{
    EnterCriticalSection(&g_CritSect);
}


void CCriticalSection::Leave()
{
    LeaveCriticalSection(&g_CritSect);
}


由于篇幅有限关键代码段就说到这里,接下来向大家简单介绍一下内核方式下的同步处理。


  哎呀!这下可就惨了,这可是要说好多的哦!书上的罗罗嗦嗦我就不说了,我就说一些我平时的运用吧。首先内核对象和一般的我们使用的对象是不一样的,这样的一些对象我们可以简单理解为特殊对象。而我们内核方式的同步就是利用这样的一些特殊对象进行处理我们的同步,其中包括:事件对象,互斥对象,信号量等。对于这些内核对象我只向大家说明两点:
1.内核对象的创建和销毁
2.内核对象的等待处理和等待副作用


第一:内核对象的创建方式基本上而言都没有什么太大的差别,例如:创建事件就用HANDLE CreateEvent(…..),创建互斥对象 HANDLE CreateMutex(…….)。而大家注意的也是这三个内核对象在创建的过程中是有一定的差异的。对于事件对象我们必须明确指明对象是人工对象还是自动对象,而这种对象的等待处理方式是完全不同的。什么不同下面说(呵呵)。互斥对象比较简单没什么说的,信号量我们创建必须注意我们要定义的最大使用数量和初始化量。最大数量>初始化量。再有如果我们为我们的内核对象起名字,我们就可以在整个进程中共用,也可以被其他进程使用,只需要OPEN就可以了。也就不多说了。


第二:内核对象的等待一般情况下我们使用两个API:
DWORD WaitForSingleObject( //单个内核对象的等待
            HANDLE hHandle, // handle to object
            DWORD dwMilliseconds // time-out interval
);


DWORD WaitForMultipleObjects( //多个内核对象的等待
            DWORD nCount, // number of handles in array
            CONST HANDLE *lpHandles, // object-handle array
            BOOL fWaitAll, // wait option
            DWORD dwMilliseconds // time-out interval
);


具体怎么用查MSDN了。


  具体我们来说等待副作用,主要说事件对象。首先事件对象是分两种的:人工的,自动的。人工的等待是没有什么副作用的(也就是说等待成功后,要和其他的对象一样要进行手动释放)。而自动的就不一样,但激发事件后,返回后自动设置为未激发状态。这样造成的等待结果也不一样,如果有多个线程在进行等待事件的话,如果是人工事件,被激活后所有等待线程成执行状态,而自动事件只能有其中一个线程可以返回继续执行。所以说在使用这些内核对象的时候,要充分分析我们的使用目的,再来设定我们创建时候的初始化。简单的同步我就说到这里了。下面我就将将我们一般情况下处理游戏服务器处理过程中的数据保护问题分析:


首先向大家说说服务器方面的数据保护的重要性,图例如下:


用户列表


    用户删除


    用户数据修改


    使用数据


加入队列


  对于上面的图例大家应该也能够看出在我们的游戏服务器之中,我们要对于我们用户的操作是多么的频繁。如此频繁的操作我们如果不进行处理的话,后果将是悲惨和可怕的,举例:如果我们在一个线程删除用户的一瞬间,有线程在使用,那么我们的错误将是不可难以预料的。我们将用到了错误的数据,可能会导致服务器崩溃。再者我们多个线程在修改用户数据我们用户数据将是没有办法保持正确性的。等等情况都可能发生。怎么样杜绝这样的一些情况的发生呢?我们就必须要进行服务器数据的保护。而我们如何正确的保护好数据,才能够保持服务器的稳定运行呢?下面说一下一些实际处理中的一些经验之谈。


1.我们必须充分的判断和估计我们服务器中有那些数据要进行数据保护,这些就需要设计者和规划者要根据自己的经验进行合理的分析。例如:在线用户信息列表,在线用户数据信息,消息列表等。。。。。


2.正确和十分小心的保护数据和正确的分析要保护的数据。大家知道我们要在很多地方实现我们的保护措施,也就是说我们必须非常小心谨慎的来书写我们的保护,不正确的保护会造成系统死锁,服务器将无法进行下去(我在处理的过程中就曾经遇到过,头都大了)。正确的分析要保护的数据,也就是说,我们必须要估计到我们要保护的部分的处理能够比较快的结束。否则我们必须要想办法解决这个问题:例如:


DATA_STRUCT g_data;
CRITICAL_SECTION g_cs;


EnterCriticalSection(&g_cs);
SendMessage(hWnd,WM_ONEMSG,&g_data,0);
LeaveCriticalSection(&g_cs);


以上处理就有问题了,因为我们不知道SendMessage()什么时候完成,可能是1/1000豪秒,也可能是1000年,那我们其他的线程也就不用活了。所以我们必须改正这种情况。


DATA_STRUCT g_data;
CRITICAL_SECTION g_cs;


EnterCriticalSection(&g_cs);
PostMessage(hWnd,WM_ONEMSG,&g_data,0);
LeaveCriticalSection(&g_cs);


或者 DATA_STRUCT temp_data;


EnterCriticalSection(&g_cs);
temp_data = g_cs;
LeaveCriticalSection(&g_cs);
SendMessage(hWnd,WM_ONEMSG,& temp_data,0);


3.最好不要复合保护用户数据,这样可能会出现一些潜在的死锁
Unity网络游戏Linux服务器部署实战:Mirror组件与Headless模式 网络同步是构建多人在线游戏的核心技术,它通过客户端与服务器之间的数据交换,确保所有玩家在虚拟世界中看到一致的游戏状态。其原理通常基于状态同步或帧同步,技术价值在于为玩家提供实时互动体验,是MMO、MOBA等游戏类型的基石。在工程实践中,开发者需要选择合适的网络库并完成服务端部署,才能让游戏从本地测试走向线上服务。Mirror作为Unity的高层网络API,封装了底层连接与RPC调用,显著降低了开发门槛。而将游戏服务器部署至Linux环境,则是实现稳定线上服务的关键步骤,涉及Headless模式构建、进程守护 阅读详情

相关推荐

游戏后端开发:构建可扩展的多人在线游戏服务器

本文介绍了游戏后端开发的基本原理和步骤。通过选择适当的编程语言和框架,创建服务器和路由,实现游戏逻辑,持久化数据,并考虑扩展性、性能优化和安全性,可以构建出稳定、可扩展的多人在线游戏服务器。在游戏开发中,后端服务器的角色至关重要。本文将介绍如何构建一个可扩展的多人在线游戏服务器,并提供相关的源代码示例。对于多人在线游戏,需要将游戏状态持久化到数据库,以便在服务器重启或玩家断线后能够恢复游戏进度。首先,我们需要创建一个服务器来处理游戏客户端的请求。在游戏后端开发中,安全性是一个重要的考虑因素。

YOLOv7895的博客 531

网络游戏的服务器在哪个文件夹,手里有一份网络游戏的服务端(预言OL) 现在缺少GM命令,在服务端里也找不到command这个文件...

满意答案dickinass2013.09.27采纳率:56%等级:12已帮助:11553人/movemap 9998 83 92 飞至关岛/movemap 1000 260 362 飞至预言城/movemap 4000 396 516 飞至雪域/movemap 3000 253 294 飞至海底/movemap 2000 359 242 飞至无名岛/movemap 1030 244 305...

weixin_36048947的博客 1128

Windows CE平台下的C语言网络游戏开发实战源码

本文还有配套的精品资源,点击获取 简介:本项目聚焦于在Windows CE环境下使用C语言进行网络游戏开发。Windows CE是一个适用于嵌入式设备的操作系统,项目内容包含作者在开发中遇到的问题及解决方案,适合初学者学习C语言在实际项目中的应用,尤其是在游戏开发领域的运用。源码不仅限于网络游戏,还包括其他C语言项目,为学习者提供不同领域的编程实践。其中的“CE点滴.htm...

weixin_28872035的博客 1630

网络游戏服务器开发框架浅谈

在游戏开发中一般不会将所有的服务放到一个服务器上,都会采取针对项目的各个部分分布在各自独立的服务器中。 例如手游服务框架: 以上其实表达的是很是简陋的,所以最近看了一份比较详细的服务器架构文章,分享一下希望对大家有些用处。 总共将分为10个模块: LoginGate服务器 LoginServer服务器 GameGate服务器 GameServer服务器 IMServer服务器 ...

风云莫测的博客 1988

深度可分离卷积(DepthwiseSeparableConvolution):Depthwise卷积与Pointwise卷积

深度可分离卷积不用多说,在轻量级网络架构方面是一个绕不开的话题,只要接触深度学习多多少少会接触。深度可分离卷积即DepthwiseSeparableConvolution,该卷积将一个常规卷积过程划分成两个完成Depthwise卷积和Pointwise卷积,在保证输出一样时,计算量大大降低。首先来看,一个常规卷积是怎么实现的。其示意图如果我们从通道数变换的角度来看,卷积就是将3通道的tensor变为了4通道的tensor,我们来看看深度可分离卷积是怎么实现通道的变化的。...

xu.hyj 1万+

STM32与SD卡SPI接口硬件设计:从原理图到调试的完整指南

SPI(串行外设接口)作为一种广泛应用的同步串行通信协议,以其接线简单、驱动成熟的特点,成为连接微控制器与各类外设的经典方案。其工作原理基于主从架构,通过时钟、数据线和片选信号实现全双工通信,在嵌入式系统中具有极高的技术价值,常用于连接传感器、存储设备和显示模块等。在数据存储场景中,SD卡凭借其大容量和便携性成为首选,而通过SPI模式驱动SD卡,则平衡了性能与设计复杂度。本文聚焦于使用Altium Designer进行硬件设计的工程实践,详细解析了STM32微控制器与SD卡通过SPI通信时,电源管理、上拉电

weixin_34101784的博客 555

网络游戏服务器开发

当今网络游戏在中国大陆已经在大范围的蔓延,暂且不论这样的一种趋势会带来什么样的游戏产业趋势。这里只就网络游戏的制作和大家进行交流,同时将自己的制作经验写处理,希望为中国的游戏业的发展做出一点点的贡献。。网络游戏的程序开发从某种意义上来看,最重要的应该在于游戏服务器端的设计和制作。对于服务器端的制作。将分为以下几个模块进行:1.网络通信模块2.协议模块3.线程池模块4.内存管理模块5.游戏规

surehui的专栏 2230

K-GAS解读(08)

大众kgas标准,大众及其供应商kgas

m0_51113317的博客 598

游戏后台开发技术全面解析

游戏后台技术,通常指的是游戏服务器和与之相关的所有技术组件。这些技术组件共同工作,为游戏提供必要的数据处理、存储、同步等功能。游戏后台技术的核心目标是确保游戏在多用户环境中的稳定性、安全性和流畅性。

极客代码 3603

Android逆向实战:抖音聊天记录数据库解析与导出(附完整SQL查询)

本文提供了一份Android逆向实战指南,详细解析了抖音聊天记录数据库的结构与导出方法。通过定位核心的`im.db`数据库文件,深度解读conversation_list、msg等关键表结构,并附上完整的SQL查询语句,帮助开发者或研究人员安全、合规地提取和分析本地聊天数据,适用于移动安全研究与数据分析场景。

mango的博客 860

跨平台Socket库:项目的核心功能/场景

跨平台Socket库:项目的核心功能/场景 去发现同类优质开源项目:https://gitcode.com/ 跨平台Socket库-Windows与Linux兼容,轻松实现网络通信开发 项目介绍 在这个日益数字化的时代,网络通信已经成为软件开发的基石。然而,跨平台开发始终是技术人员面临的一大挑战。为了解决这一问题,跨平台Socket库应运而生。本库专注于为Windows和Linux环境下的网络通信...

gitblog_06758的博客 363

基于STM32、HAL库的TLV320AIC3204IRHBR音频接口芯片驱动程序设计

* 开启所有数字部分 */#define TLV320AIC3204_REG_LEFT_HEADPHONE_OUT_VOLUME 0x0C /* 左声道耳机输出音量 */#define TLV320AIC3204_REG_DIGITAL_AUDIO_PATH_CONTROL 0x05 /* 数字音频路径控制 */#define TLV320AIC3204_REG_ANALOG_HP_FILTER_CONTROL 0x34 /* 模拟耳机滤波器控制 *//* 最大音量 */

corlin6688的博客 522

网络游戏通讯模型初探(二)

  网络游戏通讯模型初探(二) 网络互连  在了解了网络游戏基本形态之后,让我们进入真正的实际应用部分。首先,作为网络游戏,除了常规的单机游戏所必需的东西之外,我们还需要增加一个网络通讯模块,当然,这也是网络游戏较为主要的部分,我们来讨论一下如何实现网络的通讯模块。  一个完善的网络通讯模块涉及面相当广,本文仅对较为基本的处理方式进行讨论。网络游戏是由客户端和服务器组成,相应也需要两种

429

Python 爬虫实战:爬取 GitHub 热门项目(星数 / 语言 / 作者)

本文系统介绍了使用Python爬虫技术获取GitHub热门项目数据的完整方案。通过分析GitHub Trending页面和官方API两种数据源,详细讲解了HTML解析、JSON数据处理、反爬应对策略等关键技术。采用requests、BeautifulSoup、pandas等工具链,实现了项目信息(名称、星数、语言等)的结构化爬取,并配套数据清洗、统计分析及可视化功能。特别强调遵守GitHub API使用规范,包括Token认证、请求频率控制等合规要求。该方案为开发者提供了一种高效、合法的数据采集方法,适用于

2503_91057718的博客 2346

YOLOv11涨点改进 | 首发全网创新、主干改进篇 | AAAI 2026 | PartialNet 主干让 YOLOv11 更加强大!引入部分通道机制 和 部分注意力卷积,全方面提升了模型的性能

本文提出PartialNet主干网络改进YOLOv11的方法,通过部分通道机制(PCM)和部分注意力卷积(PATConv)提升模型效率。PartialNet将特征图通道拆分并应用不同操作,显著减少计算量和内存占用。主要创新包括:1)部分通道机制实现计算优化;2)三种注意力模块(PATch/PATsp/PATsf)增强特征提取;3)动态部分卷积(DPConv)自适应调整计算复杂度。文章详细提供了代码实现和YOLOv11集成方法。

Ai缝合怪、助力小伙伴高效发论文 268

游戏服务器端寻路方案:基于Recast&Detour的跨平台编译与集成实践

寻路算法是游戏开发中的核心技术,尤其在MMO、SLG等多人游戏中,它决定了角色移动的智能性与流畅度。其核心原理通常基于A*等图搜索算法,在导航网格上计算最优路径。这项技术的价值在于为游戏AI提供自主移动能力,是构建沉浸式虚拟世界的基石。在应用场景上,寻路不仅用于NPC行为控制,更是多人游戏同步、反作弊等服务器端权威逻辑的关键支撑。本文聚焦于**跨平台编译**与**服务端集成**,深入探讨如何将高性能的Recast & Detour C++寻路库,从源码编译为Windows、Linux等多平台原生库,并通过P

weixin_30564785的博客 485

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

高性能的网络游戏服务器的设计

高性能的网络游戏服务器的设计 说起高性能的网络游戏,有2个典范,1个是暴雪的WOW,另外一个要数腾讯的QQGame了,因为对于MMPRPG的体系接触不深,几乎属于文盲,没有太多的发言权,而自己又是搞休闲游戏开发的所以本文就主要谈谈QQGame了。 前些天通过朋友得到了QQGame的一个系统分析的文档,看完后很是震惊,彻底被QQ的设计所折服了,到底是千万人在线系统经验的拥有者

兴趣是最好的老师 957

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

teamspeak搭建_在Windows下快速配置TeamSpeak服务器

现在玩网络游戏都喜欢用语音交流,大家都登录到一个语音聊天的房间里,有问题直接说话就可以了,还省去打字的麻烦。不过现在提供免费的语音聊天平台不是很多,虽然有一些语音聊天软件提供免费的平台,但是不能申请自己房间,只能去公共房间聊天。其实我们只要有个稳定的服务器,就可以来自己架设语音聊天平台,如果自己的电脑配置不错、网速快、经常在线,还也在自己的电脑上架设语音聊天平台。TeamSpeak是目前最流行的语...

weixin_35537968的博客 2143

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

Unity网络游戏Linux服务器部署实战:Mirror组件与无头模式详解

网络同步是实时多人游戏的核心技术,它通过权威服务器模式确保游戏状态的一致性,防止作弊并提升玩家体验。其原理在于客户端与服务器之间通过序列化数据交换,实现游戏对象状态、事件和指令的同步。这项技术的价值在于为开发者提供了构建稳定、可扩展在线游戏的基础架构。在应用场景上,无论是竞技游戏、MMORPG还是合作体验,都依赖于可靠的网络同步框架。本文聚焦于Unity引擎中Mirror组件的实战部署,深入探讨如何在Linux服务器环境下配置无头模式,解决环境差异、端口绑定和权限管理等常见问题,并分享systemd服务配置

weixin_33871366的博客 432

从 36.5.3 到 37.3.6:victory-scatter 包的版本演进与现代化改造全解析

Victory 是 React 生态中一套可组合的交互式数据可视化组件库,而 `victory-scatter` 正是其中负责绘制散点图(Scatter Chart)与气泡图(Bubble Chart)的核心包。本文以 [packages/victory-scatter/CHANGELOG.md](https://link.gitcode.com/i/e421a8254438a8438098282

gitblog_00782的博客 941

java毕业设计社区微服务平台小程序服务器端Mybatis+系统+数据库+调试部署

springboot基于Springboot的大学宿舍管理系统。springboot基于B_S模式的后勤管理系统-在线报修系统。springboot基于微服务架构的图书借阅系统的设计与实现。springboot基于springboot的社会公益平台。springboot物流车辆规费管理系统的设计与实现。springboot基于智能推荐的良品店铺平台。基于SSM框架的校园爱心公益平台的设计与实现。

bishe503的博客 188

网络游戏开发:从核心设计到技术实现的完整指南

网络游戏开发是一个系统工程,其核心在于将创意转化为可执行的蓝图。从技术原理层面看,关键在于理解网络同步方案(如状态同步与帧同步)的选择,这直接决定了游戏的手感与架构。状态同步将逻辑计算集中在服务器,利于反作弊与稳定;帧同步则依赖客户端确定性计算,追求低延迟下的极致流畅。在工程实践中,合理的技术选型(如Unity/UE引擎、Java/Go服务器)与架构前瞻性设计,能有效支撑游戏的长线运行。数据管理策略(如MySQL与Redis的配合)与客户端性能优化(如渲染合批与资源动态加载)是保障体验的基石。这些基础技术最

weixin_33851177的博客 389

Unity网络游戏开发:基于Mirror组件实现同步与Linux服务器部署

网络同步是实时多人游戏开发的核心技术,它确保了不同客户端之间游戏状态的一致性。其原理基于客户端-服务器架构,通过权威服务器模型,所有关键逻辑在服务器执行,再通过状态同步机制将结果分发至各客户端。这项技术的价值在于为玩家提供公平、流畅的多人互动体验,是构建MMO、竞技对战等在线游戏的基础。在工程实践中,Unity开发者常借助Mirror这类开源网络库来高效实现同步逻辑,它提供了Command、ClientRpc和SyncVar等核心组件,简化了开发流程。其应用场景广泛覆盖了从独立游戏到中小型商业项目的多人联机

weixin_33889245的博客 333

windows系统和苹果系统游戏互通么

随着云计算技术的发展和个人终端性能的不断提升,未来我们有理由相信会有越来越多优质的游戏作品打破传统意义上的平台局限,为广大用户提供更加丰富多元化的娱乐选择。而作为消费者而言,掌握正确的知识和技巧则有助于更好地享受这一趋势所带来的便利。希望以上内容能够帮助大家顺利解决Windows系统与苹果系统间游戏互通方面遇到的各种难题!

x3088的博客 5146
上一篇: 在Mysql数据库中查看和修改字符集
下一篇: 谈谈游戏服务器架构设计
Webols
博客等级 码龄16年 9粉丝 8原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值