最新文章专题视频专题问答1问答10问答100问答1000问答2000关键字专题1关键字专题50关键字专题500关键字专题1500TAG最新视频文章推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37视频文章20视频文章30视频文章40视频文章50视频文章60 视频文章70视频文章80视频文章90视频文章100视频文章120视频文章140 视频2关键字专题关键字专题tag2tag3文章专题文章专题2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章专题3
当前位置: 首页 - 正文

WPE封包教程(新手级)

来源:动视网 责编:小OO 时间:2025-09-25 13:55:06
文档

WPE封包教程(新手级)

WPE封包教程(新手级)游戏数据格式和存储:在进行我们的工作之前,我们需要掌握一些关于计算机中储存数据方式的知识和游戏中储存数据的特点。本章节是提_D5A_KOM!`供给菜鸟级的玩家看的,如果你是高手就可以跳过了,如果,你想成为无坚不摧的剑客,那么,这些东西就会花掉你一些0w@~ynW[时间;如果,你只想作个江湖的游客的话,那么这些东西,了解与否无关紧要。是作剑客,还是作游客,你选择吧!现在我们开始!首先,你要知道游戏中储存数据的几种格式,这几种格式是:字节(BYTE)、字(WORD)和双字(
推荐度:
导读WPE封包教程(新手级)游戏数据格式和存储:在进行我们的工作之前,我们需要掌握一些关于计算机中储存数据方式的知识和游戏中储存数据的特点。本章节是提_D5A_KOM!`供给菜鸟级的玩家看的,如果你是高手就可以跳过了,如果,你想成为无坚不摧的剑客,那么,这些东西就会花掉你一些0w@~ynW[时间;如果,你只想作个江湖的游客的话,那么这些东西,了解与否无关紧要。是作剑客,还是作游客,你选择吧!现在我们开始!首先,你要知道游戏中储存数据的几种格式,这几种格式是:字节(BYTE)、字(WORD)和双字(
WPE封包教程(新手级)

游戏数据格式和存储:

  在进行我们的工作之前,我们需要掌握一些关于计算机中储存数据方式的知识和游戏中储存数据的特点。本章节是提 _D5A_KOM!` 供给菜鸟级的玩家看的,如果你是高手就可以跳过了,如果,你想成为无坚不摧的剑客,那么,这些东西就会花掉你一些 0 w@~ynW[ 时间;如果,你只想作个江湖的游客的话,那么这些东西,了解与否无关紧要。是作剑客,还是作游客,你选择吧!

  现在我们开始!首先,你要知道游戏中储存数据的几种格式,这几种格式是:字节(BYTE)、字(WORD)和双字(DOUBLEOmKT}D~_ 4 WORD),或者说是8位、16位和32位储存方式。字节也就是8位方式能储存0~255的数字;字或说是16位储存方式能储存= 0~65535的数;双字即32位方式能储存0~4294967295的数。G]q6Ika__ 

  为何要了解这些知识呢?在游戏中各种参数的最大值是不同的,有些可能100左右就够了,比如,金庸群侠传中的角 oSy[/Y44_a 

色的等级、随机遇敌个数等等。而有些却需要大于255甚至大于65535,象金庸群侠传中角色的金钱值可达到数百万。所以 _UyK|KL,在游戏中各种不同的数据的类型是不一样的。在我们修改游戏时需要寻找准备修改的数据的封包,在这种时候,正确判 i~l0XjQb_s 断数据的类型是迅速找到正确地址的重要条件。t_ _;y>q__

  在计算机中数据以字节为基本的储存单位,每个字节被赋予一个编号,以确定各自的位置。这个编号我们就称为地址 __+J+_]P\\: })T_D\\2__M。CQ3;NY=o 

  在需要用到字或双字时,计算机用连续的两个字节来组成一个字,连续的两个字组成一个双字。而一个字或双字的地 =_Qt&_B) FV:{l_C{h~ 

址就是它们的低位字节的地址。现在我们常用的Windows9x操作系统中,地址是用一个32位的二进制数表示的。而在平 W%~ __S~wx e2/&_X;2__ 时我们用到内存地址时,总是用一个8位的16进制数来表示它。

1Qf5H!5vx (l_EWnf=2二进制和十六进制又是怎样一回事呢?cWh Aj>?_Q

u_4+)l_v_t   简单说来,二进制数就是一种只有0和1两个数码,每满2则进一位的计数进位法。同样,16进制就是每满十六就进一 _ _|H

]X+| 位的计数进位法。16进制有0--F十六个数字,它为表示十到十五的数字采用了A、B、C、D、E、F六个数字,它们和十进制 _0__g`W_Re 的对应关系是:A对应于10,B对应于11,C对应于12,D对应于13,E对应于14,F对应于15。而且,16进制数和二进制数间 VWbgusxJ_ 有一个简单的对应关系,那就是;四位二进制数相当于一位16进制数。比如,一个四位的二进制数1111就相当于16进制的 q(a6@6f"kD F,1010就相当于A。N_I_V&)`w 

  了解这些基础知识对修改游戏有着很大的帮助,下面我就要谈到这个问题。由于在计算机中数据是以二进制的方式储 w3,QT}WvY 存的,同时16进制数和二进制间的转换关系十分简单,所以大部分的修改工具在显示计算机中的数据时会显示16进制的代 _ !X_ _|Tf 码,而且在你修改时也需要输入16进制的数字。你清楚了吧?Qv']*C_[!z 

s__E iA __  在游戏中看到的数据可都是十进制的,在要寻找并修改参数的值时,可以使用Windows提供的计算器来进行十进制和 P#76eh_R]K #16进制的换算,我们可以在开始菜单里的程序组中的附件中找到它。_7_\\5 _[lM 

  现在要了解的知识也差不多了!不过,有个问题在游戏修改中是需要注意的。在计算机中数据的储存方式一般是低位 O:_k_@'&__ 数储存在低位字节,高位数储存在高位字节。比如,十进制数41715转换为16进制的数为A2F3,但在计算机中这个数被存 o5AyJuS-u$ 为F3A2。|_)G_E7y0Q 

  看了以上内容大家对数据的存贮和数据的对应关系都了解了吗?好了,接下来我们要告诉大家在游戏中,封包到底 _[+_ 1(_[# 是怎么一回事了,来!大家把袖口卷起来,让我们来干活吧! ._B>_|>W O

二:什么是封包?[wIyW/__+ 

  怎么截获一个游戏的封包?怎么去检查游戏服务器的ip地址和端口号?Internet用户使用的各种信息服务,其通讯 "T_#c#?___ 的信息最终均可以归结为以IP包为单位的信息传送,IP包除了包括要传送的数据信息外,还包含有信息要发送到的目的IP `^|m_N_

h 地址、信息发送的源IP地址、以及一些相关的控制信息。当一台路由器收到一个IP数据包时,它将根据数据包中的目的IP *__/?L_\\_7 地址项查找路由表,根据查找的结果将此IP数据包送往对应端口。下一台IP路由器收到此数据包后继续转发,直至发到目 /WM_G)#kw' 的地。路由器之间可以通过路由协议来进行路由信息的交换,从而更新路由表。e#&[4tQF 

  那么我们所关心的内容只是IP包中的数据信息,我们可以使用许多监听网络的工具来截获客户端与服务器之间的交换 Og-M nx3_ 数据,下面就向你介绍其中的一种工具:WPE。AB+_Zc ]_ 

  WPE使用方法:执行WPE会有下列几项功能可选择:m__0_/J3 

  SELECTGAME选择目前在记忆体中您想拦截的程式,您只需双击该程式名称即可。@yobT,_DXi 

  TRACE追踪功能。用来追踪撷取程式送收的封包。WPE必须先完成点选欲追踪的程式名称,才可以使用此项目。按下 _hFQC%N. ' Play键开始撷取程式收送的封包。您可以随时按下||暂停追踪,想继续时请再按下||。按下正方形可以停止撷取封 XwlUk_w "q 包并且显示所有已撷取封包内容。若您没按下正方形停止键,追踪的动作将依照OPTION里的设定值自动停止。如果您没有 w )_D_O"Z7 撷取到资料,试试将OPTION里调整为WinsockVersion2。WPE及Trainers是设定在显示至少16bits颜色下才可执行 vR+(7__^Yy 。

_* Q51'?y 

  FILTER过滤功能。用来分析所撷取到的封包,并且予以修改。US> m1Ks_X

  SENDPACKET送出封包功能。能够让您送出假造的封包。)_Tb{_O_ 

  TRAINERMAKER制作修改器。/R t/E_fu 

  OPTIONS设定功能。让您调整WPE的一些设定值。_T5_Pc_2_R 

  FILTER的详细教学_E*___RP 8 

  -当FILTER在启动状态时,ON的按钮会呈现红色。-当您启动FILTER时,您随时可以关闭这个视窗。FILTER将会保 Tz_X>d_  NORMALMODE:D:.^]o[__ 

  范例:G._<9_K9K

  当您在StreetFighterOnline﹝快打旋风线上版﹞游戏中,您使用了两次火球而且击中了对方,这时您会撷取到 __Zb___9& 以下的封包:SEND->0000081421060104SEND->00000209870067FFA4AA112200000000SEND->`o:)PTQNg 0000038411091109SEND->00000A09C1100000FF5244SEND->00000A09C1100000665244u_y`U__1>

  您的第一个火球让对方减了16滴﹝16=10h﹞的生命值,而您观察到第4跟第5个封包的位置4有10h的值出现,应该就 &s] s]

V) 是这里了。c~$ipX_ __ 

  您观察10h前的0A09C1在两个封包中都没改变,可见得这3个数值是发出火球的关键。:ye)%UU"|: 

  因此您将0A09C110填在搜寻列﹝SEARCH﹞,然后在修改列﹝MODIFY﹞的位置4填上FF。如此一来,当您再度发出火 Dq-h`lh!D# 球时,FF会取代之前的10,也就是攻击力为255的火球了!

-3_w?_ y 

  ADVANCEDMODE:

R[__7ab]A 

  范例:当您在一个游戏中,您不想要用真实姓名,您想用修改过的假名传送给对方。在您使用TRACE后,您会发现有 _y()#FRp7

 些封包里面有您的名字出现。假设您的名字是Shadow,换算成16进位则是﹝5368616F77﹞;而您打算用moon﹝6DA\\Sb_uRty_ 6F6F6E2020﹞来取代他。1)SEND->00000814210601042)SEND->00000106995368616F770001c)Y_ I3G$_ 053)SEND->00000384110911094)SEND->00000A09C110005368616F7700115)SEND->00000A_:X ;8 $.z 09C1100000665244R'9_TD=qEK 

  但是您仔细看,您的名字在每个封包中并不是出现在相同的位置上bAW;2_ _NB 

  -在第2个封包里,名字是出现在第4个位置上-在第4个封包里,名字是出现在第6个位置上B!__8]_\\D 

  在这种情况下,您就需要使用ADVANCEDMODE-您在搜寻列﹝SEARCH﹞填上:5368616F77﹝请务必从位置1开 _Q2tGe~H 始填﹞-您想要从原来名字Shadow的第一个字母开始置换新名字,因此您要选择从数值被发现的位置开始替代连续数值﹝ Q_)Ppx7) fromthepositionofthechainfound﹞。-现在,在修改列﹝MODIFY﹞000的位置填上:6D6F6F6E2020﹝此为 {yf_G___J 相对应位置,也就是从原来搜寻栏的+001位置开始递换﹞-如果您想从封包的第一个位置就修改数值,请选择﹝fromthebu$5_gGWVf beginningofthepacket﹞-R:_1-0I$ 

  了解一点TCP/IP协议常识的人都知道,互联网是将信息数据打包之后再传送出去的。每个数据包分为头部信息和数据 SmtH2%yI 信息两部分。头部信息包括数据包的发送地址和到达地址等。数据信息包括我们在游戏中相关操作的各项信息。那么在做 c| p eR_O. 截获封包的过程之前我们先要知道游戏服务器的IP地址和端口号等各种信息,实际上最简单的是看看我们游戏目录下,是 2{&|%1_Jg_ 否有一个SERVER.INI的配置文件,这个文件里你可以查看到个游戏服务器的IP地址,比如金庸群侠传就是如此,那么除了 VT+_G_m__S 这个我们还可以在DOS下使用NETSTAT这个命令,Z\\@__vN[_[ NETSTAT命令的功能是显示网络连接、路由表和网络接口信息,可以让用户得知目前都有哪些网络连接正在运作。或 ,Cx5( ~k_U 者你可以使用木马客星等工具来查看网络连接。工具是很多的,看你喜欢用哪一种了。_FUH_a"$Bg 

  NETSTAT命令的一般格式为:NETSTAT[选项]_;q__zCo_e 

  命令中各选项的含义如下:-a显示所有socket,包括正在监听的。-c每隔1秒就重新显示一遍,直到用户中断它。 ~_6@~_fhu_ 

-i显示所有网络接口的信息。-n以网络IP地址代替名称,显示出网络连接情形。-r显示核心路由表,格式同"route- Hm4lR{_A e"。-t显示TCP协议的连接情况。-u显示UDP协议的连接情况。-v显示正在进行的工作。

/T_tI_ R_>

三:怎么来分析我们截获的封包?`O_R_DN|s6 

  首先我们将WPE截获的封包保存为文本文件,然后打开它,这时会看到如下的数据(这里我们以金庸群侠传里PK店小 (&X"_~:nm2 

二客户端发送的数据为例来讲解):3,i_L#_+t 

  第一个文件:SEND->0000E6560D227E6BE417131312131213671BSEND->00101712DD341212LscAsq0000E6561EF1290617123B0E171ASEND->0000E6561BC06812126_2Jn8DwAT 

125ASEND->0000E65602C813C97E6BE417103527131212SEND->0000E65617C912~_ocd4,d= 

  第二个文件:SEND->0000833368471B0E8172767677767776027ESEND->00107277071C7777M: `FZ}&_L 77777277727777776DSEND->000083337B944C6372775E6B72F3SEND->000083337EA5217777~ |!___q>z

773FSEND->0000833367AD76CF1B0E8172755042767777SEND->0000833372AC776bqJM_#y@ 

  我们发现两次PK店小二的数据格式一样,但是内容却不相同,我们是PK的同一个NPC,为什么会不同呢?原来金庸群 3b PVK_sY_ 侠传的封包是经过了加密运算才在网路上传输的,那么我们面临的问题就是如何将密文解密成明文再分析了。gXf_~zxS_ 

  因为一般的数据包加密都是异或运算,所以这里先讲一下什么是异或。简单的说,异或就是"相同为0,不同为1"( 8q0f_#/_`v 这是针对二进制按位来讲的),举个例子,0001和0010异或,我们按位对比,得到异或结果是0011,计算的方法是:0001 BkO"__{__ 的第4位为0,0010的第4位为0,它们相同,则异或结果的第4位按照"相同为0,不同为1"的原则得到0,0001的第3位为0, ___~I'Z=Wo 0010的第3位为0,则异或结果的第3位得到0,0001的第2位为0,0010的第2位为1,则异或结果的第2位得到1,0001的第1 _s:fnOMv " 位为1,0010的第1位为0,则异或结果的第1位得到1,组合起来就是0011。异或运算今后会遇到很多,大家可以先熟悉熟 ? th_+~_dE 悉,熟练了对分析很有帮助的。Kfj*#_) SZ 

  下面我们继续看看上面的两个文件,按照常理,数据包的数据不会全部都有值的,游戏开发时会预留一些字节空间来 _:F<_a~_k_ 便于日后的扩充,也就是说数据包里会存在一些"00"的字节,观察上面的文件,我们会发现文件一里很多"12",文件二里 JP@U_vDE| 很多"77",那么这是不是代表我们说的"00"呢?推理到这里,我们就开始行动吧!C6, B_qlio 

  我们把文件一与"12"异或,文件二与"77"异或,当然用手算很费事,我们使用"M2M1.0加密封包分析工具"来计算就 _w]%r]PwU+ 

方便多了。得到下面的结果:9r_)5d&,6_ 

  第一个文件:1SEND->0000F4441F306C79F6050101000100017509SEND->00100500CF260000'dht5iI;Yw 000005001C0000002SEND->0000F4440CE33B130500291C05083SEND->0000F44409D27A00V5A7_w V3~ 0000484SEND->0000F44410DA01DB6C79F6050227350100005SEND->0000F44405DB00_mV' d9(s? 

  第二个文件:1SEND->0000F4441F306C79F6050101000100017509SEND->00100500706B0000o= 8yp2vG 00000500050000001A2SEND->0000F4440CE33B130500291C05843SEND->0000F44409D25600O0Z'vb__FG 0000484SEND->0000F44410DA01B86C79F6050227350100005SEND->0000F44405DB00Sa@Xh,_y Z 

  哈,这一下两个文件大部分都一样啦,说明我们的推理是正确的,上面就是我们需要的明文!_R8-_H_j E 

  接下来就是搞清楚一些关键的字节所代表的含义,这就需要截获大量的数据来分析。jwE_<}y I

  首先我们会发现每个数据包都是"F444"开头,第3个字节是变化的,但是变化很有规律。我们来看看各个包的长度, D_ _#_

_A9 发现什么没有?对了,第3个字节就是包的长度!通过截获大量的数据包,我们判断第4个字节代表指令,也就是说客户 3 Z__bvf_^ 端告诉服务器进行的是什么操作。例如向服务器请求战斗指令为"30",战斗中移动指令为"D4"等。接下来,我们就需要 JY_' __d,O 分析一下上面第一个包"F4441F306C79F60501010001000175090500CF260000000005001C002R1W[,_Ga! 0000",在这个包里包含什么信息呢?应该有通知服务器你PK的哪个NPC吧,我们就先来找找这个店小二的代码在什么 /

Db~ -$_K 地方。我们再PK一个小喽罗(就是大理客栈外的那个咯):SEND->0000F4441F30D475F605010100010001?vk &k(FT 7509SEND->001005008A1900000000110002000000C0我们根据常理分析,游戏里的NPC种类虽然不会超 s_NN_t0q( 过65535(FFFF),但开发时不会把自己在字的范围,那样不利于游戏的扩充,所以我们在双字里看看。通过"店小二 :0vNg:u_+_ "和"小喽罗"两个包的对比,我们把目标放在"6C79F605"和"CF260000"上。(对比一下很容易的,但你不能太迟钝 l~\\'Z2op _ 咯,呵呵)我们再看看后面的包,在后面的包里应该还会出现NPC的代码,比如移动的包,游戏允许观战,服务器必然需 K_!'9_w__t 要知道NPC的移动坐标,再广播给观战的其他玩家。在后面第4个包"SEND->0000F44410DA01DB6C79F6050227x_0 j$]$_ 35010000"里我们又看到了"6C79F605",初步断定店小二的代码就是它了!(这分析里边包含了很多工作的,大家 gT_s5xDvJ 可以用WPE截下数据来自己分析分析)__4_|_*_mC 

  第一个包的分析暂时就到这里(里面还有的信息我们暂时不需要完全清楚了)2_vK{_Yw _ 

  我们看看第4个包"SEND->0000F44410DA01DB6C79F605022735010000",再截获PK黄狗的包,(狗会 ]ul]L R%._ 出来2只哦)看看包的格式:SEND->0000F4441ADA020B4B7DF605022735010000SEND->0010EB03F83 "c_AwU9_ 05022736010000# ___Z_8  根据上面的分析,黄狗的代码为"4B7DF605"(100040011),不过两只黄狗服务器怎样分辨呢?看看"EB03F8|w__WBV{^ 05"(100140011),是上一个代码加上100000,呵呵,这样服务器就可以认出两只黄狗了。我们再通过野外遇敌截获的数 <_M_s,0YKx 据包来证实,果然如此。:__+Y+5:U] 

  那么,这个包的格式应该比较清楚了:第3个字节为包的长度,"DA"为指令,第5个字节为NPC个数,从第7个字节开始 qVE6ROS_h 的10个字节代表一个NPC的信息,多一个NPC就多10个字节来表示。 6 [w_ /_X" 

  大家如果玩过网金,必然知道随机遇敌有时会出现增援,我们就利用游戏这个增援来让每次战斗都会出现增援的NPC 

__vm 1vX; 吧。4_ZSc'9e9 

  通过在战斗中出现增援截获的数据包,我们会发现服务器端发送了这样一个包:F44412E9EB03F805020000QS\\H_[?M_$ 03000000000000第5-第8个字节为增援NPC的代码(这里我们就简单的以黄狗的代码来举例)。那么,我们就利用 _Hj2E-RwG 单机代理技术来同时欺骗客户端和服务器吧!PqKbG_<}Y

  好了,呼叫NPC的工作到这里算是完成了一小半,接下来的事情,怎样修改封包和发送封包,我们下节继续讲解吧。w_|lA%H7`J 

四:怎么冒充"客户端"向"服务器"发我们需要的封包?@FO= 0_;y 

  这里我们需要使用一个工具,它位于客户端和服务器端之间,它的工作就是进行数据包的接收和转发,这个工具我们 {(J__bgsxm 称为代理。如果代理的工作单纯就是接收和转发的话,这就毫无意义了,但是请注意:所有的数据包都要通过它来传输, r om_`%qp^ 这里的意义就重大了。我们可以分析接收到的数据包,或者直接转发,或者修改后转发,或者压住不转发,甚至伪造我们 (J_enTL`%u 需要的封包来发送。_a=_@]O_v/ 

  下面我们继续讲怎样来同时欺骗服务器和客户端,也就是修改封包和伪造封包。通过我们上节的分析,我们已经知 UZvF5Hoe+O 道了打多个NPC的封包格式,那么我们就动手吧!Z__P-^_10 

wB'GV1|jL 

  首先我们要查找客户端发送的包,找到战斗的特征,就是请求战斗的第1个包,我们找"F4441F30"这个特征,这是 _H__. o=4[ 不会改变的,当然是要解密后来查找哦。找到后,表示客户端在向服务器请求战斗,我们不动这个包,转发。继续向下 b,__h@._s 查找,这时需要查找的特征码不太好办,我们先查找"DA",这是客户端发送NPC信息的数据包的指令,那么可能其他包也 8SGqD

aR_t 有"DA",没关系,我们看前3个字节有没有"F444"就行了。找到后,我们的工作就开始了!\\gC__h'3 

3R___>"X c

  我们确定要打的NPC数量。这个数量不能很大,原因在于网金的封包长度用一个字节表示,那么一个包可以有255个字 v]h^0W__U 节,我们上面分析过,增加一个NPC要增加10个字节,所以大家算算就知道,打20个NPC比较合适。~v>3lEG

n* 

  然后我们要把客户端原来的NPC代码分析计算出来,因为增加的NPC代码要加上100000哦。再把我们增加的NPC代码计 KOhK#t>H@0 算出来,并且组合成新的封包,注意代表包长度的字节要修改啊,然后转发到服务器,这一步在编写程序的时候要注意算 q:cC_k#ra_ 法,不要造成较大延迟。!&adO,jN+= 

  上面我们欺骗服务器端完成了,欺骗客户端就简单了。 ___}__IRD! 

  发送了上面的封包后,我们根据新增NPC代码构造封包马上发给客户端,格式就是"F44412E9NPC代码020000a9__KoOa.H 

03000000000000把每个新增的NPC都构造这样一个包,按顺序连在一起发送给客户端,客户端也就被我们骗过了 <:_>[24LJ{ ,很简单吧。k x%_\\_Cz 

  以后战斗中其他的事我们就不管了,尽情地开打吧。 _l0r^LK_$_ 

游戏外挂基本原理及实现I_p0`R+_8_ 

解释游戏外挂的基本原理和实现方法m_~_tv{#Y 

游戏外挂已经深深地影响着众多网络游戏玩家,今天在网上看到了一些关于游戏外挂编写的技术,于是转载上供大家参考J_ u"/#_@ 

  1、游戏外挂的原理8CU_l |I ~ 

  外挂现在分为好多种,比如模拟键盘的,鼠标的,修改数据包的,还有修改本地内存的,但好像没有修改服务器内存 Ow {NI-^_K 的哦,呵呵。其实修改服务器也是有办法的,只是技术太高一般人没有办法入手而已。(比如请GM去夜总会、送礼、收黑 +.a->SZ_5" 钱等等办法都可以修改服务器数据,哈哈)w_ x,gth*p 

  修改游戏无非是修改一下本地内存的数据,或者截获API函数等等。这里我把所能想到的方法都作一个介绍,希望大 ${tBu#_$-d 家能做出很好的外挂来使游戏厂商更好的完善自己的技术。我见到一篇文章是讲魔力宝贝的理论分析,写得不错,大概是 _2 U_3WH.o 那个样子。下来我就讲解一下技术方面的东西,以作引玉之用。^_= _'+#|: 

  2技术分析部分h@_{CMe_ 

  2.1模拟键盘或鼠标的响应4EJ6Zy![0* 

  我们一般使用:6_z5?9I4_[ 

  UINTSendInput(5xc-Mk_IRL 

    UINTnInputs,  //countofinputeventslsOZ%p%fV_ 

   �PINPUTpInputs, //arrayofinputevents-l$-\\(,M`# 

    intcbSize    //sizeofstructuretH9BC5+r} 

  );UzUt=s!^H_ 

  API函数。第一个参数是说明第二个参数的矩阵的维数的,第二个参数包含了响应事件,这个自己填充就可以,最后 K_[i|OZW_u 是这个结构的大小,非常简单,这是最简单的方法模拟键盘鼠标了,呵呵。注意,这个函数还有个替代函数:xk_kW?[__& 

  VOIDkeybd_event(,__7izr_f8 

    BYTEbVk,       //虚拟键码__t_ _5__ 

    BYTEbScan,      //扫描码QP4___`r#, 

    DWORDdwFlags,%/=#8_v4_* 

    ULONG_PTRdwExtraInfo //附加键状态___[&p__^h 

  );_&_B>YiA_

 

  与6"_U_)d7^_ 

  VOIDmouse_event(7_= _x]p__ 

    DWORDdwFlags,     //motionandclickoptionsz__q__&,KZ 

    DWORDdx,       //horizontalpositionorchangerW_MG_e_P: 

    DWORDdy,        //verticalpositionorchangenyR4E}@_:O 

    DWORDdwData,      //wheelmovement1?bX$$y l; 

    ULONG_PTRdwExtraInfo  //application-definedinformation_&GH _,i_s 

  );b Z_EyP W 

  这两个函数非常简单了,我想那些按键精灵就是用的这个吧。上面的是模拟键盘,下面的是模拟鼠标的。这个仅仅是 _1f_^4J~{ 模拟部分,要和游戏联系起来我们还需要找到游戏的窗口才行,或者包含快捷键,就象按键精灵的那个激活键一样,我们 7J_@_D})si 可以用GetWindow函数来枚举窗口,也可以用Findwindow函数来查找制定的窗口(注意,还有一个FindWindowEx), `+/xA\\X] FindwindowEx可以找到窗口的子窗口,比如按钮,等什么东西。当游戏切换场景的时候我们可以用FindWindowEx来确定一 *n=NBkq%/! 些当前窗口的特征,从而判断是否还在这个场景,方法很多了,比如可以GetWindowInfo来确定一些东西,比如当查找不 &S_"o_ jbb 到某个按钮的时候就说明游戏场景已经切换了,等等办法。有的游戏没有控件在里面,这是对图像做坐标变换的话,这种 jWd 7>1R? 方法就要受到了。这就需要我们用别的办法来辅助分析了。=-c_"~__ 4 

  至于快捷键我们要用动态连接库实现了,里面要用到hook技术了,这个也非常简单。大家可能都会了,其实就是一个 FasA f_( 3 全局的hook对象然后SetWindowHook就可以了,回调函数都是现成的,而且现在网上的例子多如牛毛。这个实现在外挂中 O&_!_tW^ih 已经很普遍了。如果还有谁不明白,那就去看看MSDN查找SetWindowHook就可以了。_%  不要低估了这个动态连接库的作用,它可以切入所有的进程空间,也就是可以加载到所有的游戏里面哦,只要用对, _XQu_~/{A= 你会发现很有用途的。这个需要你复习一下Win32编程的基础知识了。呵呵,赶快去看书吧。'Dq_!o[2y 

  2.2截获消息_38_dXfl__ 

  有些游戏的响应机制比较简单,是基于消息的,或者用什么定时器的东西。这个时候你就可以用拦截消息来实现一些 $up.< qzj_ 有趣的功能了。}Lx?RU+_@= 

_ b]s*z<|%

  我们拦截消息使用的也是hook技术,里面包括了键盘消息,鼠标消息,系统消息,日志等,别的对我们没有什么大的 Z@=_1_-l_ 用处,我们只用拦截消息的回调函数就可以了,这个不会让我写例子吧。其实这个和上面的一样,都是用SetWindowHook hbh___h m 来写的,看看就明白了很简单的。&__inu _mc 

  至于拦截了以后做什么就是你的事情了,比如在每个定时器消息里面处理一些我们的数据判断,或者在定时器里面在 BF_2,E<^A 模拟一次定时器,那么有些数据就会处理两次,呵呵。后果嘛,不一定是好事情哦,呵呵,不过如果数据计算放在客户端 _Z&%#,0>] 的游戏就可以真的改变数据了,呵呵,试试看吧。用途还有很多,自己想也可以想出来的,呵呵。P_=eVp(/x_ 

  2.3拦截Socket包1_vBR\\!d?7 

  这个技术难度要比原来的高很多。$ +;`[b __ 

  首先我们要替换WinSock.DLL或者WinSock32.DLL,我们写的替换函数要和原来的函数一致才行,就是说它的函数输出 V2cLw_Q'0 什么样的,我们也要输出什么样子的函数,而且参数,参数顺序都要一样才行,然后在我们的函数里面调用真正的 S,m)yh__._ WinSock32.DLL里面的函数就可以了。bR6_.Xdt.n 

  首先:我们可以替换动态库到系统路径。);_V6___YE 

  其次:我们应用程序启动的时候可以加载原有的动态库,用这个函数LoadLibary然后定位函数入口用GetProcAddress ,_F(nkb_t 函数获得每个真正Socket函数的入口地址。jXkz,]Iy 

  当游戏进行的时候它会调用我们的动态库,然后从我们的动态库中处理完毕后才跳转到真正动态库的函数地址,这样 NS @j`6/U_ 我们就可以在里面处理自己的数据了,应该是一切数据。呵呵,兴奋吧,拦截了数据包我们还要分析之后才能进行正确的 Il_J6_&__9 应答,不要以为这样工作就完成了,还早呢。等分析完毕以后我们还要仿真应答机制来和服务器通信,一个不小心就会被 YU.aZdA&V3 封号。1X_2MhV 

  分析数据才是工作量的来源呢,游戏每次升级有可能加密方式会有所改变,因此我们写外挂的人都是亡命之徒啊,被 yjlX@YXnw_ 人愚弄了还不知道。M

eo(|U_ 

  2.4截获API_m(SGE,("w 

  上面的技术如果可以灵活运用的话我们就不用截获API函数了,其实这种技术是一种补充技术。比如我们需要截获 ;Qk U__W<( Socket以外的函数作为我们的用途,我们就要用这个技术了,其实我们也可以用它直接拦截在Socket中的函数,这样更直 H)__z_}6[` 接。>Q#_h,x~vu

  现在拦截API的教程到处都是,我就不列举了,我用的比较习惯的方法是根据输入节进行拦截的,这个方法可以用到 &7_xr.c_7 任何一种操作系统上,比如Windows98/2000等,有些方法不是跨平台的,我不建议使用。这个技术大家可以参考 /a__p3>xkt 《Windows核心编程》里面的545页开始的内容来学习,如果是Win98系统可以用“Windows系统奥秘”那个最后一章来学习 ;_:2]_++G 。_:b t;DJ@_ 

网络游戏通讯模型初探 f__v@.vnn. 

 [文章导读]F_yle_K+D? 

本文就将围绕三个主题来给大家讲述一下网络游戏的网络互连实现方法Bv-_|#sdxm 

序言 

LF__6PKS 

  网络游戏,作为游戏与网络有机结合的产物,把玩家带入了新的娱乐领域。网络游戏在中国开始发展至今也仅有3,4 _h[eC _i 年的历史,跟已经拥有几十年开发历史的单机游戏相比,网络游戏还是非常年轻的。当然,它的形成也是根据历史变化而 Vx'82C__IC 产生的可以说没有互联网的兴起,也就没有网络游戏的诞生。作为新兴产物,网络游戏的开发对广大开发者来说更加神秘 Y=G9|7_*lO ,对于一个未知领域,开发者可能更需要了解的是网络游戏与普通单机游戏有何区别,网络游戏如何将玩家们连接起来, >qE f991SZ 以及如何为玩家提供一个互动的娱乐环境。本文就将围绕这三个主题来给大家讲述一下网络游戏的网络互连实现方法。Q___[PVk_Z 

 网络游戏与单机游戏 I:|<};m m 

  说到网络游戏,不得不让人联想到单机游戏,实际上网络游戏的实质脱离不了单机游戏的制作思想,网络游戏和单机 z_v%]j0_ ? 游戏的差别大家可以很直接的想到:不就是可以多人连线吗?没错,但如何实现这些功能,如何把网络连线合理的融合进 z_t___S:1\\ 单机游戏,就是我们下面要讨论的内容。在了解网络互连具体实现之前,我们先来了解一下单机与网络游戏它们各自的运 _(r6_'q0[ 行流程,只有了解这些,你才能深入网络游戏开发的核心。 s78MX_S?py 现在先让我们来看一下普通单机游戏的简化执行流程: kJ'rt_z4QO 

Initialize()//初始化模块 `{KdmW_hW 

{ Sj'Iz _#_ 

 初始化游戏数据; .I__U\\_wN_ 

} "M;aNi_^B_ 

Game()//游戏循环部分 PQd*)6K:A_ 

{ \\;_ vo_BU 

 绘制游戏场景、人物以及其它元素; _{ 1eW_*9 

 获取用户操作输入; =v_s]__Kmm 

 switch(用户输入数据) _h_`n)_ b 

 { _9,?7mgZ p 

  case移动: ]*_Cq'_  { __S[;d\\Z]~ 

   处理人物移动; G"F)_t(_iX 

  } uXouN_$& 

  break; e_?_7_Oom 

  case攻击: _60R_]_Q__ 

  { M_    处理攻击逻辑: gX|_We}_H 

  } _l3_xI\\{jn 

  break; ~f:"Q(

f_+ 

  ... _h _e[_2_, 

  其它处理响应; __"

-Yj__~ 

  ... }60__/5HNr 

  default: I_}_+9@d

   break; ZY|$[

_>X!

 } kIR_/.Ij} 

 游戏的NPC等逻辑AI处理; OH_13@_k_ 

} BS=~G+/:_| 

Exit()//游戏结束 _\\30rF]F`l 

{ __8} ,_ : 

 释放游戏数据; Q__b_5@e# 

 离开游戏; je`In_n<__

}_^'n;W<\\p)

  我们来说明一下上面单机游戏的流程。首先,不管是游戏软件还是其他应用软件,初始化部分必不可少,这里需要对 `n _RF"T__ 游戏的数据进行初始化,包括图像、声音以及一些必备的数据。接下来,我们的游戏对场景、人物以及其他元素进行循环 N_wVhJ_do 绘制,把游戏世界展现给玩家,同时接收玩家的输入操作,并根据操作来做出响应,此外,游戏还需要对NPC以及一些逻 \

&l@rMD3s 辑AI进行处理。最后,游戏数据被释放,游戏结束。 1ASoH,D_/_ 

 网络游戏与单机游戏有一个很显著的差别,就是网络游戏除了一个供操作游戏的用户界面平台(如单机游戏)外,还需 cb_Y__Q';{ 要一个用于连接所有用户,并为所有用户提供数据服务的服务器,从某些角度来看,游戏服务器就像一个大型的数据库, _os7x wI;T 提供数据以及数据逻辑交互的功能。让我们来看看一个简单的网络游戏模型执行流程: W_4bN']_?_ 

客户机: _{ _ _'402 

Login()//登入模块 1:r#m- __\\ 

{ G___{{M' 1 

 初始化游戏数据; tL_xeq?Oo] 

 获取用户输入的用户和密码; M[+#*f.T}_ 

 与服务器创建网络连接; "LSzF__m_K 

 发送至服务器进行用户验证; 9 r&JsC_c 

 ... M_%Ksyr9 

 等待服务器确认消息; [_9B1_%W_ 

 ... N:okt_)q:% 

 获得服务器反馈的登入消息; _RehraY3q 

 if(成立) _i]s%tE_Z1 

  进入游戏; 1/~=61_msc 

 else a_

j,o  提示用户登入错误并重新接受用户登入; FE]U_qB_ 

} mU~&__o_U_ 

Game()//游戏循环部分 GKj_tX_?~1 

{ JT!9LNh;R` 

 绘制游戏场景、人物以及其它元素; TVx `&C__+ 

 获取用户操作输入; {_~u Ti>U

 将用户的操作发送至服务器; _#9Jr?K43 

 ... \\gB ~0@[\\7 

 等待服务器的消息; dq`{f_qG_l 

 ... ]@__ke_' " 

 接收服务器的反馈信息; _Kv_o_&_:_ 

 switch(服务器反馈的消息数据) 1_+WVh_7gF 

 { l_yF_;5|?z 

  case本地玩家移动的消息: J{_-`&I_'b 

  { _Eu:/U*j 

   if(允许本地玩家移动) DyCzRk_H_ 

    客户机处理人物移动; ^/#_G,MxNy 

   else lZ.lf.{F 

    客户机保持原有状态; _*d;_TpwUI 

  } d +,!p8_Q 

   break; !5C"`@}q>

  case其他玩家/NPC的移动消息: kx

?Yin8K_ 

  { Z_|$Dch_C 

   根据服务器的反馈信息进行其他玩家或者NPC的移动处理; @R50M_ (@W 

  } G^_#>H

E|

 

  break; S_Fd__k9_ 

  case新玩家加入游戏: _aG^E__^^Y 

  { cI_@qt>&

   在客户机中添加显示此玩家; m2jts(st_p 

  } A_1-_,b.Ni 

   break;   case玩家离开游戏: x@Z{5__w_a 

  { _Q|_h$

D~ 

   在客户机中销毁此玩家数据; KFHZ3HZ:>

  } O_5-GrR^yt 

   break; Ovk=s,a)K 

  ... sV,Yz3E  其它消息类型处理; `_x/i1^/_@ 

  ...  Od_~uYOL/B 

  default: 4~h _0/H" 

   break; +5*bU_1}_O 

 } %'s __ =r` 

} B_2Y.1mXq_ 

Exit()//游戏结束 _o/_o6|[=3 

{ G`O*A_Q}[ 

 发送离开消息给服务器; s0 ;a _j ... lr_~ |=}^ 

 等待服务器确认; E qz|eS*6 

 ... w_=___;>__

 得到服务器确认消息; s_=_(_q#

 与服务器断开连接; ; >_H1_A__

 释放游戏数据; m8__{8r>6*

 离开游戏; _|I8+(_~) 

}u__M1$_3<

  服务器:G_h=I2GSo 

Listen()  //游戏服务器等待玩家连接模块 f_u_b04x_) 

{ 0)d=_'__3S 

 ... u*2_?_Gky 

 等待用户的登入信息; ,M{__G_ X_ 

 ... bI:_W4y>I=

 接收到用户登入信息; #_) ~u _YQ 

 分析用户名和密码是否符合; __,_3_Sf_? 

 if(符合) vq(#I

_h2_ 

 { T}^3_Re`i 

  发送确认允许进入游戏消息给客户机;  d_"Zu1___0 

  把此玩家进入游戏的消息发布给场景中所有玩家; ?_H_

PAX 

  把此玩家添加到服务器场景中; ! _Ea >tQ|

 } _*`_"+J_ _ 

 else \\_,p@r_]Q 

 { gQ0,KYmI3_ 

  断开与客户机的连接;1}la__ )lC 

 } _ni{'V4A_ 

} J$yq#LBbR@ 

Game() //游戏服务器循环部分 7OmT^jV_2 

{ n_ktG_O__ 

 ... *_KDTB_ d 

 等待场景中玩家的操作输入; !0?o_3,of- 

 ... 'k\\j[fk/_K 

 接收到某玩家的移动输入或NPC的移动逻辑输入;YRV h[Bqg` 

 //此处只以移动为例 __o_+{,_>t

 进行此玩家/NPC在地图场景是否可移动的逻辑判断; /g_F)msUF_ 

 if(可移动) wJ2_cAX;"_ 

 { Z(|$[GZP[ 

  对此玩家/NPC进行服务器移动处理; _BH {z_]a 

  发送移动消息给客户机; < e BmCr_J

  发送此玩家的移动消息给场景上所有玩家; h+B7BjA>G

 } ~*1>)P8]#

 else U AXp;_W` 

  发送不可移动消息给客户机;b>p_w%d[[J

} 1 jB0__gNe 

Exit()  //游戏服务=器结束 `Z,WK__us_ 

{ #_1_$4 接收到玩家离开消息; / Q@4___HV 

 将此消息发送给场景中所有玩家; 6w & 发送允许离开的信息; j}.gK_6Yq* 

 将玩家数据存入数据库; __7Wm_L__C 

 注销此玩家在服务器内存中的数据; \\wz^__Z{_U 

} k_IS_ )_*_ 

}_5(C_I_n_l 

  让我们来说明一下上面简单网络游戏模型的运行机制。先来讲讲服务器端,这里服务器端分为三个部分(实际上一个 %52e_^,// 完整的网络游戏远不止这些):登入模块、游戏模块和登出模块。登入模块用于监听网络游戏客户端发送过来的网络连接 }\\<=B%{__ 消息,并且验证其合法性,然后在服务器中创建这个玩家并且把玩家带领到游戏模块中;游戏模块则提供给玩家用户实 >b\\|%=(x!* 际的应用服务,我们在后面会详细介绍这个部分;在得到玩家要离开游戏的消息后,登出模块则会把玩家从服务器中删 \\,_N_T5_> 除,并且把玩家的属性数据保存到服务器数据库中,如:经验值、等级、生命值等。 ___d_Y$n_w 

  接下来让我们看看网络游戏的客户端。这时候,客户端不再像单机游戏一样,初始化数据后直接进入游戏,而是在与 _W!

ug^2" 服务器创建连接,并且获得许可的前提下才进入游戏。除此之外,网络游戏的客户端游戏进程需要不断与服务器进行通讯 C\\"C12_n{ ,通过与服务器交换数据来确定当前游戏的状态,例如其他玩家的位置变化、物品掉落情况。同样,在离开游戏时,客户 k"[AV2UW1_ 端会向服务器告知此玩家用户离开,以便于服务器做出相应处理。 _^_{RtP#_= 以上用简单的伪代码给大家阐述了单机游戏与网络游戏的执行流程,大家应该可以清楚看出两者的差别,以及两者间相互 gF9_GU5T_: 的关系。我们可以换个角度考虑,网络游戏就是把单机游戏的逻辑运算部分搬移到游戏服务器中进行处理,然后把处理结 "(_H%_m_9K 果(包括其他玩家数据)通过游戏服务器返回给连接的玩家。 &Pv$n_MB$I 

网络互连 O '#FV_Z.g 

  在了解了网络游戏基本形态之后,让我们进入真正的实际应用部分。首先,作为网络游戏,除了常规的单机游戏所必 x_

K[

 [b 需的东西之外,我们还需要增加一个网络通讯模块,当然,这也是网络游戏较为主要的部分,我们来讨论一下如何实现网 ?:GrM!kq76 络的通讯模块。 "<^ Vp_-7r

  一个完善的网络通讯模块涉及面相当广,本文仅对较为基本的处理方式进行讨论。网络游戏是由客户端和服务器组成 "7( @I^'t6 ,相应也需要两种不同的网络通讯处理方式,不过也有相同之处,我们先就它们的共同点来进行介绍。我们这里以 

gi_: M_=_ MicrosoftWindows2000[2000Server]作为开发平台,并且使用Winsock作为网络接口(可能一些朋友会考虑使用 8

j]QnH0& DirectPlay来进行网络通讯,不过对于当前在线游戏,DirectPlay并不适合,具体原因这里就不做讨论了)。 iR_W5*-66f 

  确定好平台与接口后,我们开始进行网络连接创建之前的一些必要的初始化工作,这部分无论是客户端或者服务器都 uuUj IZCtz 需要进行。让我们看看下面的代码片段:2mx }_bj8_ 

WORDwVersionRequested;S]&iWSADATAwsaData;Rz_B6_4

 

wVersionRequestedMAKEWORD(1,1);g_s_@

^u#O 

if(WSAStartup(wVersionRequested,&wsaData)!0) N_p_~q_tR 

{ l_29AC}^ 

 Failed(WinSockVersionError!"); __aO}_G0r(xP? 

.L]2g$W_\\p 

  上面通过调用Windows的socketAPI函数来初始化网络设备,接下来进行网络Socket的创建,代码片段如下: Y k"yup@_3 

SOCKETsSocketsocket(AF_INET,m_lProtocol,0);z8};(_I_>)

if(sSocket==INVALID_SOCKET)B)_-P#_ ,} 

{ 8w_S___9%+ 

 Failed("WinSocketCreateError!"); :~_,ak_X$_ 

}y2C/DyuAY| 

  这里需要说明,客户端和服务端所需要的Socket连接数量是不同的,客户端只需要一个Socket连接足以满足游戏的需 afcyA_zIB& 要,而服务端必须为每个玩家用户创建一个用于通讯的Socket连接。当然,并不是说如果服务器上没有玩家那就不需要创 %A_( __hmC 建Socket连接,服务器端在启动之时会生成一个特殊的Socket用来对玩家创建与服务器连接的请求进行响应,等介绍网络 4__Rv_}Y d 监听部分后会有更详细说明。 _3j$,x(ua9 

  有初始化与创建必然就有释放与删除,让我们看看下面的释放部分: '8L_c}

-M4 

if(sSocket!=INVALID_SOCKET)_{S5D~A*a+ 

{ "_ []J[!}x 

 closesocket(sSocket); $U5$*R@jo[ 

} 2m;

if(WSACleanup()!=0) _1^=__[k_ 

{ s}__Sx__l0 

 Warning("Can'treleaseWinsocket");d`E_m_) 3v 

}___^0)T@_ 

  这里两个步骤分别对前面所作的创建初始化进行了相应释放。 A,#hYi=_-, 

  接下来看看服务器端的一个网络执行处理,这里我们假设服务器端已经创建好一个Socket供使用,我们要做的就是让 _FwG__MrJW 这个Socket变成监听网络连接请求的专用接口,看看下面代码片段:Pi_|oO_-_M 

SOCKADDR_INaddr; 4__.mb___W 

memset(&addr,0,sizeof(addr)); _r\\_66]u_[ 

addr.sin_family=AF_INET; EMejvPnZO 

addr.sin_addr.s_addr=htonl(INADDR_ANY); >taZ_w '_

addr.sin_port=htons(Port); //Port为要监听的端口号 __Q_,_`__Y 

//绑定socket )p;gm`42oY 

if(bind(sSocket,(SOCKADDR*)&addr,sizeof(addr))==SOCKET_ERROR) __6Gs,-Kb: 

{ d_fq5P!_' 

 Failed("WinSocketBindError!"); $_bK_a"_T* 

} ZvO:!u0+" 

//进行监听 .*g;2.-qv& 

if(listen(sSocket,SOMAXCONN)==SOCKET_ERROR) #B)`dA_0a_ 

{ uL4_@_e_ 

 Failed("WinSocketListenError!"); g%<_7Px[W 

}Z@_8amT;

  这里使用的是阻塞式通讯处理,此时程序将处于等待玩家用户连接的状态,倘若这时候有客户端连接进来,则通过 _{w^fliz

Y accept()来创建针对此玩家用户的Socket连接,代码片段如下:j{ __P_,(- 

sockaddraddrServer;}%R6Su_]_y 

intnLensizeof(addrServer);__uj/le0__ 

SOCKETsPlayerSocketaccept(sSocket,&addrServer,&nLen);95__wV+ q* 

if(sPlayerSocket==INVALID_SOCKET) >8tE_`2[i*

{nhCB ])u8l 

 Failed(WinSocketAcceptError!");_ "rjJ"u 1 

}

y9_)l,@D 

  这里我们创建了sPlayerSocket连接,此后游戏服务器与这个玩家用户的通讯全部通过此Socket进行,到这里为止, Z gU;_=.__ 我们服务器已经有了接受玩家用户连接的功能,现在让我们来看看游戏客户端是如何连接到游戏服务器上,代码片段如下 !Xj m h$_F :vP~F+z @g 

SOCKADDR_INaddr; t5_83Q/1_@ 

memset(&addr,0,sizeof(addr)); ga~vQ__7I_ 

addr.sin_family=AF_INET;//要连接的游戏服务器端口号 1k/l_7_&n" 

addr.sin_addr.s_addr=inet_addr(IP);//要连接的游戏服务器IP地址, (wY% $_kW4 

addr.sin_port=htons(Port);//到此,客户端和服务器已经有了通讯的桥梁, _%mY_IXsuH 

//接下来就是进行数据的发送和接收: 462ae` 6l 

connect(sSocket,(SOCKADDR*)&addr,sizeof(addr)); if(send(sSocket,pBuffer,lLength,0)==SOCKET_ERROR) b{_dzbm_ak 

{ 8DI|+`OgW 

 Failed("WinSocketSendError!"); R{rV1j#@!a 

}G[^G~U\\+! 

  这里的pBuffer为要发送的数据缓冲指针,lLength为需要发送的数据长度,通过这支SocketAPI函数,我们无论在客 s,29____z7 户端或者服务端都可以进行数据的发送工作,同时,我们可以通过recv()这支SocketAPI函数来进行数据接收:ql__xW

@_| 

if(recv(sSocket,pBuffer,lLength,0)==SOCKET_ERROR) 0*"j:V__ 

{ A__fU 7'B_ 

 Failed("WinSocketRecvError!");#_}_Q+Fw =Xw_ 

  其中pBuffer用来存储获取的网络数据缓冲,lLength则为需要获取的数据长度。 ]_V_H@\\_ f 

  现在,我们已经了解了一些网络互连的基本知识,但作为网络游戏,如此简单的连接方式是无法满足网络游戏中百人 iQIw]_*h^ 千人同时在线的,我们需要更合理容错性更强的网络通讯处理方式,当然,我们需要先了解一下网络游戏对网络通讯的需 _9C_ _0__5 求是怎样的。 kC__5_,_yj 

  大家知道,游戏需要不断循环处理游戏中的逻辑并进行游戏世界的绘制,上面所介绍的Winsock处理方式均是以阻塞 u_"h/)C'H_ 方式进行,这样就违背了游戏的执行本质,可以想象,在客户端连接到服务器的过程中,你的游戏不能得到控制,这时如 B3t_>M) 9 果玩家想取消连接或者做其他处理,甚至显示一个最基本的动态连接提示都不行。 XXc_f!~uO 

  所以我们需要用其他方式来处理网络通讯,使其不会与游戏主线相冲突,可能大家都会想到:创建一个网络线程来 _/xb__ZC{R 处理不就可以了?没错,我们可以创建一个专门用于网络通讯的子线程来解决这个问题。当然,我们游戏中多了一个线程 ,b(_S_

=_r ,我们就需要做更多的考虑,让我们来看看如何创建网络通讯线程。 $)_]_F_Cuv 

  在Windows系统中,我们可以通过CreateThread()函数来进行线程的创建,看看下面的代码片段:_kte.E%.PE 

DWORDdwThreadID; UjOhaj "h 

HANDLEhThread=CreateThread(NULL,0,NetThread/*网络线程函式*/,sSocket,0,&dwThreadID); Q_.c~I}yV 

if(hThread==NULL) I_ 47GQho_ 

{ }_a1Sfl@`3 

 Failed("WinSocketThreadCreateError!"); z\\8yB`

8b^ 

} N p__ND_/_ 

`A_ _8n_W) 

  这里我们创建了一个线程,同时将我们的Socket传入线程函数:_U"Hqu_o 

$,,>R[_;w

DWORDWINAPINetThread(LPVOIDlParam)?E__Q^n3U$ 

x_0h3j_w+6 

{__u SOCKETsSocket(SOCKET)lParam;__H_U[n_N* 

 ...|ch^eb

^7" 

 return0;Rw\\C___0' 

}^%__%5____ 

  NetThread就是我们将来用于处理网络通讯的网络线程。那么,我们又如何把Socket的处理引入线程中?   看看下面的代码片段:c\MsVH2 | 

HANDLEhEvent; _

I>%S4Z+o

hEvent=CreateEvent(NULL,0,0,0); a_R_)en{_W 

//设置异步通讯 $C0_5__iD 

if(WSAEventSelect(sSocket,hEvent, >U~|R=_*__

FD_ACCEPT|FD_CONNECT|FD_READ|FD_WRITE|FD_CLOSE)==SOCKET_ERROR) _6Z7pz__tk 

{ q#(/*Ao_U 

 Failed("WinSocketEventSelectError!"); %+xw_k=_%* 

}8__IOJ]:_w 

JOdwv4(3_V 

  通过上面的设置之后,WinSockAPI函数均会以非阻塞方式运行,也就是函数执行后会立即返回,这时网络通讯会以 PyMV_TP4 事件方式存储于hEvent,而不会停顿整支程式。 Aa`MK$_29F 

  完成了上面的步骤之后,我们需要对事件进行响应与处理,让我们看看如何在网络线程中获得网络通讯所产生的事件 [X_ }@Ct 6 消息:_=_a9etF%B 

WSAEnumNetworkEvents(sSocket,hEvent,&SocketEvents); j_JUGZVM6) 

if(SocketEvents.lNetworkEvents!=0) ;v1N_

L@w* 

{o_Q %\\[_s$ 

 switch(SocketEvents.lNetworkEvents)5pM&h~__M_ 

 {__^XBzZ!h| 

  caseFD_ACCEPT: KZ__F0_rW_ 

   WSANETWORKEVENTSSocketEvents; %AG1oWWc>.

   break;dI^IK__

__ 

  caseFD_CONNECT:!/_j,hO4Z4 

  {S9 G+#[.|

 

   if(SocketEvents.iErrorCode[FD_CONNECT_BIT]==0)_GIm " )}W 

   //连接成功  MzQ\\rg__B7 

   { oZ_vA~]x9\\ 

   //连接成功后通知主线程(游戏线程)进行处理r{\\_BbUnf) 

   }IKi{_Xh]\\_ 

  }~_x|a_oozL 

   break;9_0_/v__JN 

  caseFD_READ:KD% T__xK 

  //获取网络数据_eJX___i, 

  { ,~_4H{{   if(recv(sSocket,pBuffer,lLength,0)==SOCKET_ERROR)_Y1{B c   {_|3}5_:_k 

    Failed("WinSocketRecvError!");_0__1udlW. 

   }]3*P:$Rq_ 

  }i__ __S% 

   break;M`GP_^Ta 

  caseFD_WRITE:''_IoC _j 

   break;3bC_+Mco_ 

  caseFD_CLOSE: _dUS _ ZNY 

   //通知主线程(游戏线程),网络已经断开_^ [k0k_(_ 

   break;N l@k_*_^ 

  default:?OcjiA@_ 

   break;Oi_fvUTl9b 

 } W>B^_____S

}79_ck__Ld9 

  这里仅对网络连接(FD_CONNECT)和读取数据(FD_READ)进行了简单模拟操作,但实际中网络线程接收到事件消息后 YN__Syi_@_ ,会对数据进行组织整理,然后再将数据回传给我们的游戏主线程使用,游戏主线程再将处理过的数据发送出去,这样一 

_x_CWz\\-; 

_<},1_Ncl

个往返就构成了我们网络游戏中的数据通讯,是让网络游戏动起来的最基本要素。 ?_ _ _-3_\\ 

  最后,我们来谈谈关于网络数据包(数据封包)的组织,网络游戏的数据包是游戏数据通讯的最基本单位,网络游戏 $&h_N*7_Ts 一般不会用字节流的方式来进行数据传输,一个数据封包也可以看作是一条消息指令,在游戏进行中,服务器和客户端会 dC._bt|#Oz 不停的发送和接收这些消息包,然后将消息包解析转换为真正所要表达的指令意义并执行。 __ fR_B5U' 

互动与管理 d(-_Ec_Y>?

  说到互动,对于玩家来说是与其他玩家的交流,但对于计算机而言,实现互动也就是实现数据消息的相互传递。前面 6cz/n 8Mg 我们已经了解过网络通讯的基本概念,它构成了互动的最基本条件,接下来我们需要在这个网络层面上进行数据的通讯。 tF7h_FL_5f 遗憾的是,计算机并不懂得如何表达玩家之间的交流,因此我们需要提供一套可让计算机了解的指令组织和解析机制,也 : "Y*<=x#2 就是对我们上面简单提到的网络数据包(数据封包)的处理机制。 M_5_g\\s;y; 为了能够更简单的给大家阐述网络数据包的组织形式,我们以一个聊天处理模块来进行讨论,看看下面的代码结构:c9wf_sa_pJ 

structtagMessage{W$_0_^(FH[ 

 longlType;

5T@aCC@$h 

 longlPlayerID;A1 "SLF__Y 

};]QVNn_?PA8 

//消息指令 ? *>]")[>

//指令相关的玩家标识gNY_qAU_G5 

charstrTalk[256];//消息内容;G\\8jP_' _ 

  上面是抽象出来的一个极为简单的消息包结构,我们先来谈谈其各个数据域的用途: n_?__tN\\_M 

  首先,lType是消息指令的类型,这是最为基本的消息标识,这个标识用来告诉服务器或客户端这条指令的具体用途 _T____aE~s ,以便于服务器或客户端做出相应处理。lPlayerID被作为玩家的标识。大家知道,一个玩家在机器内部实际上也就是一 _]:>,__A@7 堆数据,特别是在游戏服务器中,可能有成千上万个玩家,这时候我们需要一个标记来区分玩家,这样就可以迅速找到特 Ae]sG_U|?' 定玩家,并将通讯数据应用于其上。 'k;rH !R 

  strTalk是我们要传递的聊天数据,这部分才是真正的数据实体,前面的参数只是数据实体应用范围的限定。 y!tC_20Q _ 

  在组织完数据之后,紧接着就是把这个结构体数据通过Socket连接发送出去和接收进来。这里我们要了解,网络在 n3_A aZp[_ 进行数据传输过程中,它并不关心数据采用的数据结构,这就需要我们把数据结构转换为二进制数据码进行发送,在接收 `]___Q:-h_ 方,我们再将这些二进制数据码转换回程序使用的相应数据结构。让我们来看看如何实现:VD_$$_Gn*q 

tagMessageMsg;_QQWadVQo_ 

Msg.lTypeMSG_CHAT;ULMu19>_

Msg.lPlayerID1000;8Z(Mvq]f&_ 

strcpy(&Msg.strTalk,"聊天信息");[Xb@ Wh:yG 

  首先,我们假设已经组织好一个数据包,这里MSG_CHAT是我们自行定义的标识符,当然,这个标识符在服务器和客 /!c_${W!sY 户端要统一。玩家的ID则根据游戏需要来进行设置,这里1000只作为假设,现在继续:tQ _S5hwm* 

char*p=(char*)&Msg; z CvKDl_L_ 

longlLength=sizeof(tagMessage);3_0S_W\\@ 

send(sSocket,p,lLength); _yU9DSY\\m{ 

//获取数据结构的长度____2 B___ 

  我们通过强行转换把结构体转变为char类型的数据指针,这样就可以通过这个指针来进行流式数据处理,这里通过 e_ J_6$_-r 

sizeof()获得结构体长度,然后用WinSock的Send()函数将数据发送出去。 {G_Q^fu;q_ 

  接下来看看如何接收数据:E_ea*__s_' 

longlLength=sizeof(tagMessage); ^zQ/mo,_Z 

char*Buffer=newchar[lLength]; V 

_6@_o]* 

recv(sSocket,Buffer,lLength); _#T'{ n1AI 

tagMessage*p=(tagMessage*)Buffer; wY[+Z_T__ 

//获取数据x8V('`_}j 

  在通过WinSock的recv()函数获取网络数据之后,我们同样通过强行转换把获取出来的缓冲数据转换为相应结构体 ` 2V19_ s] ,这样就可以方便地对数据进行访问。(注:强行转换仅仅作为数据转换的一种手段,实际应用中有更多可选方式,这里 h_YG6 pTCb 只为简洁地说明逻辑)谈到此处,不得不提到服务器/客户端如何去筛选处理各种消息以及如何对通讯数据包进行管理。 _%z,m B$LY 无论是服务器还是客户端,在收到网络消息的时候,通过上面的数据解析之后,还必须对消息类型进行一次筛选和派分, __z`f( $t[ 简单来说就是类似Windows的消息循环,不同消息进行不同处理。这可以通过一个switch语句(熟悉Windows消息循环 YUtC.TR1__ 的朋友相信已经明白此意),基于消 _R6;=n"Ueb息封包里的lType信息,对消息进行区分处理,考虑如下代码片段:

-_g_*4(w 

switch(p->lType)//这里的p->lType为我们解析出来的消息类型标识 k_e_L&b/@ 

{ s(/; U2_"e 

 caseMSG_CHAT://聊天消息 _FWrX3i___ 

  break; MyJ %`@+1 

 caseMSG_MOVE://玩家移动消息 IP__ xiV]c 

  break; C_Q__F:Rnb 

 caseMSG_EXIT://玩家离开消息 4'/nax$Bx; 

  break; _S___Jb&m- 

 default: H  break; N} G[7Rp8l 

}

H1|?t+oP 

  上面片段中的MSG_MOVE和MSG_EXIT都是我们虚拟的消息标识(一个真实游戏中的标识可能会有上百个,这就需要考 2=_,l_cWr_ 虑优化和优先消息处理问题)。此外,一个网络游戏服务器面对的是成百上千的连接用户,我们还需要一些合理的数据组 ]_7W&JKmA& 织管理方式来进行相关处理。普通的单体游戏服务器,可能会因为当机或者用户过多而导致整个游戏网络瘫痪,而这也就 HVR_ _/7&g 引入分组服务器机制,我们把服务器分开进行数据的分布式处理。 T_*Y~\\~Jhu 

  我们把每个模块提取出来,做成专用的服务器系统,然后建立一个连接所有服务器的数据中心来进行数据交互,这里 &#{Z( h.de 每个模块均与数据中心创建了连接,保证了每个模块的相关性,同时玩家转变为与当前提供服务的服务器进行连接通讯, _Ta ZmRL__ 这样就可以缓解单独一台服务器所承受的负担,把压力分散到多台服务器上,同时保证了数据的统一,而且就算某台服务 M8kPj8}_{_ 器因为异常而当机也不会影响其他模块的游戏玩家,从而提高了整体稳定性。 7_OcW C_-<

  分组式服务器缓解了服务器的压力,但也带来了服务器调度问题,分组式服务器需要对服务器跳转进行处理,就以一 _o5_Dk:Bw 个玩家进行游戏场景跳转作为讨论基础:假设有一玩家处于游戏场景A,他想从场景A跳转到场景B,在游戏中,我们称之 dP9_qSwTa_ 场景切换,这时玩家就会触发跳转需求,比如走到了场景中的切换点,这样服务器就把玩家数据从"游戏场景A服务器"删 _pNG_:_0 除,同时在"游戏场景B服务器"中把玩家建立起来。i?p$H_0b n 

  这里描述了场景切换的简单模型,当中处理还有很多步骤,不过通过这样的思考相信大家可以派生出很多应用技巧。 _ :|

>h7v 不过需要注意的是,在场景切换或者说模块间切换的时候,需要切实考虑好数据的传输安全以及逻辑合理性,否则切换很 "3X2VFwo_J .p<:II_: 6可能会成为将来玩家复制物品的桥梁。_|_B?cV_c0 E uZ总结 Y _z<3JRw_

本篇讲述的都是通过一些简单的过程来进行网络游戏通讯,提供了一个制作的思路,虽然具体实现起来还有许多要做 _52@C_9Q, ,但只要顺着这个思路去扩展、去完善,相信大家很快就能够编写出自己的网络通讯模块。由于时间仓促,本文在很多细 .<_->C?#_ 节方面都有省略,文中若有错误之处也望大家见谅。

文档

WPE封包教程(新手级)

WPE封包教程(新手级)游戏数据格式和存储:在进行我们的工作之前,我们需要掌握一些关于计算机中储存数据方式的知识和游戏中储存数据的特点。本章节是提_D5A_KOM!`供给菜鸟级的玩家看的,如果你是高手就可以跳过了,如果,你想成为无坚不摧的剑客,那么,这些东西就会花掉你一些0w@~ynW[时间;如果,你只想作个江湖的游客的话,那么这些东西,了解与否无关紧要。是作剑客,还是作游客,你选择吧!现在我们开始!首先,你要知道游戏中储存数据的几种格式,这几种格式是:字节(BYTE)、字(WORD)和双字(
推荐度:
  • 热门焦点

最新推荐

猜你喜欢

热门推荐

专题
Top