游戏开发论坛

 找回密码
 立即注册
搜索
楼主: qinglan

游戏服务器技术交流

[复制链接]

7

主题

82

帖子

110

积分

注册会员

Rank: 2

积分
110
QQ
 楼主| 发表于 2007-9-23 22:11:00 | 显示全部楼层

服务器公共组件实现 -- 发包的方式

  前面一直都在说接收数据时的处理方法,我们应该用专门的IO线程,接收到完整的消息包后加入到主线程的消息队列,但是主线程如何发送数据还没有探讨过。

  一般来说最直接的方法就是逻辑线程什么时候想发数据了就直接调用相关的socket API发送,这要求服务器的玩家对象中保存其连接的socket句柄。但是直接send调用有时候有会存在一些问题,比如遇到系统的发送缓冲区满而阻塞住的情况,或者只发送了一部分数据的情况也时有发生。我们可以将要发送的数据先缓存一下,这样遇到未发送完的,在逻辑线程的下一次处理时可以接着再发送。

  考虑数据缓存的话,那这里这可以有两种实现方式了,一是为每个玩家准备一个缓冲区,另外就是只有一个全局的缓冲区,要发送的数据加入到全局缓冲区的时候同时要指明这个数据是发到哪个socket的。如果使用全局缓冲区的话,那我们可以再进一步,使用一个独立的线程来处理数据发送,类似于逻辑线程对数据的处理方式,这个独立发送线程也维护一个消息队列,逻辑线程要发数据时也只是把数据加入到这个队列中,发送线程循环取包来执行send调用,这时的阻塞也就不会对逻辑线程有任何影响了。

  采用第二种方式还可以附带一个优化方案。一般对于广播消息而言,发送给周围玩家的数据都是完全相同的,我们如果采用给每个玩家一个缓冲队列的方式,这个数据包将需要拷贝多份,而采用一个全局发送队列时,我们只需要把这个消息入队一次,同时指明该消息包是要发送给哪些socket的即可。有关该优化的说明在云风描述其连接服务器实现的blog文章中也有讲到,有兴趣的可以去阅读一下。

149

主题

4981

帖子

5033

积分

论坛元老

Rank: 8Rank: 8

积分
5033
QQ
发表于 2007-9-23 22:23:00 | 显示全部楼层

Re:游戏服务器技术交流

晕,把不同连接的数据放到同一个buf上?那么如果是这样的情况呢?

  1. +------------------------------------+
  2. |**AAAAAAA|**BBB|AAAAA|BBBBB|********|
  3. +------------------------------------+
复制代码

图中*表示空的字节,A序列表示连接A的数据,B序列表示连接B的数据。
这时A和B连接的接下来要发送的数据包都只发送了一部分,并且互相间隔,这时要对A连接send,就只能分成2次,可是每次send的数据可能会很小,这样就意味着系统调用的频率提高了。如果是每个连接都有自己的buf,那么就可以有多少数据就send多少数据……
不知大家怎么看?

6

主题

471

帖子

1047

积分

金牌会员

Rank: 6Rank: 6

积分
1047
发表于 2007-9-24 10:13:00 | 显示全部楼层

Re:游戏服务器技术交流

全局发送队列如果碰到数据发送失败的情况,这时如何处理?
(客户端可能并没有断线,只是有几秒的瞬间网络延迟,或者recv buffer已满,暂不接受数据.
如果在10个人当中有一人发送失败,该数据就一直会在队列中?)
每个连接有自己的buffer,并使用异步发送是比较安全的.

0

主题

26

帖子

26

积分

注册会员

Rank: 2

积分
26
发表于 2007-9-24 15:04:00 | 显示全部楼层

Re:游戏服务器技术交流

lz 辛苦

7

主题

82

帖子

110

积分

注册会员

Rank: 2

积分
110
QQ
 楼主| 发表于 2007-9-24 17:06:00 | 显示全部楼层

Re: Re:游戏服务器技术交流

filx: Re:游戏服务器技术交流

全局发送队列如果碰到数据发送失败的情况,这时如何处理?
(客户端可能并没有断线,只是有几秒的瞬间网络延迟...


全局队列确实会有由于一个客户端的阻塞导致整个队列堵塞的情况

不知是否有好的解决方法,要没有的话那还是传统的方法,每个连接一个缓冲队列了

7

主题

82

帖子

110

积分

注册会员

Rank: 2

积分
110
QQ
 楼主| 发表于 2007-9-24 17:08:00 | 显示全部楼层

Re: Re:游戏服务器技术交流

sjinny: Re:游戏服务器技术交流

晕,把不同连接的数据放到同一个buf上?那么如果是这样的情况呢?
[code]
+----------------------------...


这个问题也确实存在,关于全局队列的考虑不够周全

6

主题

471

帖子

1047

积分

金牌会员

Rank: 6Rank: 6

积分
1047
发表于 2007-9-24 18:02:00 | 显示全部楼层

Re: Re: Re:游戏服务器技术交流

qinglan: Re: Re:游戏服务器技术交流



全局队列确实会有由于一个客户端的阻塞导致整个队列堵塞的情况

不知是否有好的解决方法,要没有的话那还是传统的方法,每个连接一个缓冲队列了

全局发送队列一开始也想使用,优点就是数据共享,后来实际运行中发现问题很多.
如果数据不保发送到client,可以忽略发送错误.即send后无论正确与否都直接
删除该消息.这对于UDP程序可以接受,但如果使用TCP,发送失败意味着该连接已断或者其他情况,如果等待发送(可能100-10000毫秒后才发送成功或者失败),
势必对后续队列中的消息产生延迟(100个连接每人send延迟100毫秒就很可怕了),
所以send必须是异步发送.每次pop连接buffer中的数据,异步发送数据,成功后继续pop发送余下的数据,平时待发数据都push到连接buffer中,一但连接buffer数据超过一定大小,即可认定该连接已断线或者恶意不接受数据,直接kick(当然你也可以在send超时直接kick,但client可能只是偶尔延迟几秒,这样做势必导致玩家常常掉线.).
个人认为有时候内存优化并没有解决服务器的压力,反而更增加了很多隐性bug.
因为网络是不确定,不可预测,除了AI计算,服务器瓶颈就在发送而不是内存上.

7

主题

82

帖子

110

积分

注册会员

Rank: 2

积分
110
QQ
 楼主| 发表于 2007-9-24 22:05:00 | 显示全部楼层

服务器公共组件实现 -- 状态机

  有关State模式的设计意图及实现就不从设计模式中摘抄了,我们只来看看游戏服务器编程中如何使用State设计模式。

  首先还是从mangos的代码开始看起,我们注意到登录服在处理客户端发来的消息时用到了这样一个结构体:

  struct AuthHandler
  {
    eAuthCmd cmd;
    uint32 status;
    bool (AuthSocket::*handler)(void);
  };

  该结构体定义了每个消息码的处理函数及需要的状态标识,只有当前状态满足要求时才会调用指定的处理函数,否则这个消息码的出现是不合法的。这个status状态标识的定义是一个宏,有两种有效的标识,STATUS_CONNECTED和STATUS_AUTHED,也就是未认证通过和已认证通过。而这个状态标识的改变是在运行时进行的,确切的说是在收到某个消息并正确处理完后改变的。

  我们再来看看设计模式中对State模式的说明,其中关于State模式适用情况里有一条,当操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态,这个状态通常用一个或多个枚举变量表示。

  描述的情况与我们这里所要处理的情况是如此的相似,也许我们可以试一试。那再看看State模式提供的解决方案是怎样的,State模式将每一个条件分支放入一个独立的类中。

  由于这里的两个状态标识只区分出了两种状态,所以,我们仅需要两个独立的类,用以表示两种状态即可。然后,按照State模式的描述,我们还需要一个Context类,也就是状态机管理类,用以管理当前的状态类。稍作整理,大概的代码会类似这样:

  状态基类接口:
  StateBase
  {
    void Enter() = 0;
    void Leave() = 0;
    void Process(Message* msg) = 0;
  };

  状态机基类接口:
  MachineBase
  {
    void ChangeState(StateBase* state) = 0;

    StateBase* m_curState;
  };

  我们的逻辑处理类会从MachineBase派生,当取出数据包后交给当前状态处理,前面描述的两个状态类从StateBase派生,每个状态类只处理该状态标识下需要处理的消息。当要进行状态转换时,调用MachineBase的ChangeState()方法,显示地告诉状态机管理类自己要转到哪一个状态。所以,状态类内部需要保存状态机管理类的指针,这个可以在状态类初始化时传入。具体的实现细节就不做过多描述了。

  使用状态机虽然避免了复杂的判断语句,但也引入了新的麻烦。当我们在进行状态转换时,可能会需要将一些现场数据从老状态对象转移到新状态对象,这需要在定义接口时做一下考虑。如果不希望执行拷贝,那么这里公有的现场数据也可放到状态机类中,只是这样在使用时可能就不那么优雅了。

  正如同在设计模式中所描述的,所有的模式都是已有问题的另一种解决方案,也就是说这并不是唯一的解决方案。放到我们今天讨论的State模式中,就拿登录服所处理的两个状态来说,也许用mangos所采用的遍历处理函数的方法可能更简单,但当系统中的状态数量增多,状态标识也变多的时候,State模式就显得尤其重要了。

  比如在游戏服务器上玩家的状态管理,还有在实现NPC人工智能时的各种状态管理,这些就留作以后的专题吧。

0

主题

25

帖子

25

积分

注册会员

Rank: 2

积分
25
发表于 2007-9-25 13:49:00 | 显示全部楼层

Re: 游戏服务器技术交流

楼主建个群吧。

0

主题

2

帖子

0

积分

新手上路

Rank: 1

积分
0
发表于 2007-9-26 14:25:00 | 显示全部楼层

Re:游戏服务器技术交流

支持楼主!太棒了!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-2-25 23:12

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表