在了解了网络游戏基本形态之后,让我们进入真正的实际应用部分。首先,作为网络游戏,除了常规的单机游戏所必需的东西之外,我们还需要增加一个网络通讯模块,当然,这也是网络游戏较为主要的部分,我们来讨论一下如何实现网络的通讯模块。
一个完善的网络通讯模块涉及面相当广,本文仅对较为基本的处理方式进行讨论。网络游戏是由客户端和服务器组成,相应也需要两种不同的网络通讯处理方式,不过也有相同之处,我们先就它们的共同点来进行介绍。我们这里以Microsoft Windows 2000 [2000 Server]作为开发平台,并且使用Winsock作为网络接口(可能一些朋友会考虑使用DirectPlay来进行网络通讯,不过对于当前在线游戏,DirectPlay并不适合,具体原因这里就不做讨论了)。
确定好平台与接口后,我们开始进行网络连接创建之前的一些必要的初始化工作,这部分无论是客户端或者服务器都需要进行。让我们看看下面的代码片段:
WORD wVersionRequested; WSADATAwsaData; wVersionRequested MAKEWORD(1, 1); if( WSAStartup( wVersionRequested, &wsaData ) !0 ) { Failed( WinSock Version Error!" ); } |
上面通过调用Windows的socket API函数来初始化网络设备,接下来进行网络Socket的创建,代码片段如下:
SOCKET sSocket socket( AF_INET, m_lProtocol, 0 ); if( sSocket == INVALID_SOCKET ) { Failed( "WinSocket Create Error!" ); } |
这里需要说明,客户端和服务端所需要的Socket连接数量是不同的,客户端只需要一个Socket连接足以满足游戏的需要,而服务端必须为每个玩家用户创建一个用于通讯的Socket连接。当然,并不是说如果服务器上没有玩家那就不需要创建Socket连接,服务器端在启动之时会生成一个特殊的Socket用来对玩家创建与服务器连接的请求进行响应,等介绍网络监听部分后会有更详细说明。
有初始化与创建必然就有释放与删除,让我们看看下面的释放部分:
if( sSocket != INVALID_SOCKET ) |
这里两个步骤分别对前面所作的创建初始化进行了相应释放。
接下来看看服务器端的一个网络执行处理,这里我们假设服务器端已经创建好一个Socket供使用,我们要做的就是让这个Socket变成监听网络连接请求的专用接口,看看下面代码片段:
SOCKADDR_IN addr; memset( &addr, 0, sizeof(addr) ); addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl( INADDR_ANY ); addr.sin_port = htons( Port ); // Port为要监听的端口号 // 绑定socket if( bind( sSocket, (SOCKADDR*)&addr, sizeof(addr) ) == SOCKET_ERROR ) { Failed( "WinSocket Bind Error!"); } // 进行监听 if( listen( sSocket, SOMAXCONN ) == SOCKET_ERROR ) { Failed( "WinSocket Listen Error!"); } |
这里使用的是阻塞式通讯处理,此时程序将处于等待玩家用户连接的状态,倘若这时候有客户端连接进来,则通过accept()来创建针对此玩家用户的Socket连接,代码片段如下:
sockaddraddrServer; int nLen sizeof( addrServer ); SOCKET sPlayerSocket accept( sSocket, &addrServer, &nLen ); if( sPlayerSocket == INVALID_SOCKET ) { Failed( WinSocket Accept Error!"); } |
这里我们创建了sPlayerSocket连接,此后游戏服务器与这个玩家用户的通讯全部通过此Socket进行,到这里为止,我们服务器已经有了接受玩家用户连接的功能,现在让我们来看看游戏客户端是如何连接到游戏服务器上,代码片段如下:
SOCKADDR_IN addr; memset( &addr, 0, sizeof(addr) ); addr.sin_family = AF_INET;// 要连接的游戏服务器端口号 addr.sin_addr.s_addr = inet_addr( IP );// 要连接的游戏服务器IP地址, addr.sin_port = htons( Port );//到此,客户端和服务器已经有了通讯的桥梁, //接下来就是进行数据的发送和接收: connect( sSocket, (SOCKADDR*)&addr, sizeof(addr) ); if( send( sSocket, pBuffer, lLength, 0 ) == SOCKET_ERROR ) { Failed( "WinSocket Send Error!"); } |
这里的pBuffer为要发送的数据缓冲指针,lLength为需要发送的数据长度,通过这支Socket API函数,我们无论在客户端或者服务端都可以进行数据的发送工作,同时,我们可以通过recv()这支Socket API函数来进行数据接收:
if( recv( sSocket, pBuffer, lLength, 0 ) == SOCKET_ERROR ) { Failed( "WinSocket Recv Error!"); } |
其中pBuffer用来存储获取的网络数据缓冲,lLength则为需要获取的数据长度。
现在,我们已经了解了一些网络互连的基本知识,但作为网络游戏,如此简单的连接方式是无法满足网络游戏中百人千人同时在线的,我们需要更合理容错性更强的网络通讯处理方式,当然,我们需要先了解一下网络游戏对网络通讯的需求是怎样的。