网络游戏制作:网络游戏制作技术(一)



  当今网络游戏在中国大陆已经在大范围蔓延暂且不论这样种趋势会带来什么样游戏产业趋势这里只就网络游戏制作和大家进行交流同时将自己制作经验写处理希望为中国游戏业发展做出点点贡献

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

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

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

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

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

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

1. 建立服务器端SOCKET套接字描述符点比较简单
例如:
SOCKET server_;
Server_ = (AF_INET,SOCK_STREAM,0);

2.绑定套接字server_
Const SERV_TCP_PORT = 5555;
struct sockaddr_in server_address.

mem(&server_address, 0, (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_,( struct sockaddr *)&server_address, (server_address));

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

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

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

SYSTEM_INFO system_info;
GetInfo(&system_info);

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

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

6. 产生服务器检测客户端连接并且处理线程
HANDLE hAcceptThread;
DWORD dwThreadId;
hAcceptThread= _beghreadex(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步我们完成端口句柄就可以了

参数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个也就是
2.
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort, // handle to completion port
LPDWORD lpNumberOfBytes, // s transferred
PULONG_PTR lpCompletionKey, // file completion key
LPOVERLAPPED *lpOverlapped, // buffer
DWORD dwMilliseconds // optional timeout value
);


参数1:
我们已经在前面产生完成端口句柄同时它对于客户端而言也是和客户端SOCKET连接那个端口

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

参数3:
完成端口单句柄数据指针这个指针将可以得到我们在CreateIoCompletionPort(………)中申请那片内存
借用MSDN解释:
[out] Poer 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 specied in a call to CreateIoCompletionPort.
所以在使用这个时候只需要将此处填相应数据结构空指针就可以了上面解释只有大家自己摆平了

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

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

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

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

参数4:

[out] Poer to the number of s received by this call the receive operation completes immediately. (MSDN)

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

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

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

举例如下:
const 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;
addr_Len = (client_addr);
LPHANDLE_DATA hand_Data = NULL;
while(true)
{
If((client_s=accept(server_,NULL,NULL)) SOCKET_ERROR)
{
prf(\"Accept Error: %d\",GetLastError);
0;
}
hand_Data = (LPHANDLE_DATA)malloc((HANDLE_DATA));
hand_Data-> = client_s;
(CreateIoCompletionPort((HANDLE)client_s,hCompltPort,(DWORD)hand_Data,0)NULL)
{
prf(\"CreateIoCompletionPortError: %d\", GetLastError);
}

{
game_Server->RecvDataRequest(client_s);
}
}
0;
}

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

CServerSocket : public CBaseSocket
{


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

void ControlRecvData(SOCKET client_s,char *buf, 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; //完成端口句柄
};

在上面类中是我们用来处理客户端用户请求服务器端模型

Tags:  网络游戏制作软件 如何制作网络游戏 网络游戏外挂制作 网络游戏制作

延伸阅读

最新评论

发表评论