Comet技术应用

原文转自:http://www.cnblogs.com/winnerlan/archive/2008/06/20.html

http://www.sujun.org/feed.asp?cateID=15

以前的旧文章,归下类而已.....目前也在用red5做服务器开发一款比较小的flash网络游戏,呵呵...期待...

玩家移动信息同步例子,我已经写过了两个版本的啦,一个java的,一个是fms的,现在又写了个red5的,其实这些的原理都是差不多的。以前好象没怎 么讲解原理,这些就写得详细点吧。用red5做服务器,其实客户端基本是fms一样的。(事实上就是一样,哈哈)。无非就是客户端呼叫服务端的方法,服务 端呼叫客户端的方法。(这是同步是没有采用ShareObject的,过阵可能会写吧)
先看下图片吧


首先先来看看最基本的(具体的内容我就不写了,代码里有详细的解释,在这里我只说说思路和逻辑)
程序代码 程序代码

var mync:NetConnection = new NetConnection();   mync.connect("rtmp://localhost/game");连接到服务器
mync.onStatus = function(info)
{
if (info.code == "NetConnection.Connect.Success")
{
  trace("接通");
  //通知服务器,有新用户登陆了
  this.call("userLogin",null,userName);
}
};


看这段代码。当客户端连接服务器成功后,马上呼叫服务端的用户登陆方法,并把新登陆的用户名传了过去。那么我们再看服务端

程序代码 程序代码

/**
  * 用户登陆
  * @param userName:用户名
  */
public void userLogin(String userName)
{
  System.out.println("新用户登陆:" + userName);
  //检查是否已经有用户登陆,有登陆,则呼叫其他用户接受新登陆用户的信息
  if(map.size() > 0)
  {
   for(Iterator<IServiceCapableConnection> it = map.values().iterator(); it.hasNext();)
   {
    IServiceCapableConnection ic = it.next();
    //发送用户名过去
    ic.invoke("userLogin", new Object[]{userName});
   }
  }
  //放进map对象中保存
  map.put(userName,getCurrentConn());
}


当客户端call的时候,服务器就执行userLogin里的代码。在这里个方法里服务器只做了两件事,一是把map对象里的所有的客户端连接对象进行遍 历,把新登陆用户的信息发送给所有已经登陆的客户端。然后再把当前的客户端连接对象保存在服务端的map对象中。接着来看 ic.invoke ("userLogin", new Object[]{userName})这句代码,他是呼叫客户端方法;既然服务端呼叫客户端 的userLogin方法,那么我们可以过来客户端的 userLogin方法。

程序代码 程序代码

//当有新用户登陆时,被服务器呼叫
mync.userLogin = function(name:String)
{
//根据名字复制一个新的mc,最近登陆的用户
var mc:MovieClip = _root.attachMovie("personMC","personMC" + name,depth++);
userSet.put(name,mc);
mc.setName(name);
//马上呼叫客户端,把自己的位置告诉新登陆的客户端
mync.call("userInfo",null,userName,name,personMC._x,personMC._y);
};


在客户端里,该userLogin方法也是做了两件事,一是马上根据传过来用户名,生成了有一个新的mc(在实际中就是玩家,而且真正应用中,传的还不止 一个参数),接着又呼叫服务端的一个方法userInfo,因为它必须把自己的信息通知给刚刚登陆的用户,包括自己的名字,位置等等。  接下来我们就看 服务端的userInfo方法啦

程序代码 程序代码

/**
  * 已经存在的用户的信息
  * @param userName:用户名
  * @param x:x坐标
  * @param y:y坐标
  */
public void userInfo(String selfName, String userName,double x,double y)
{
  Object[] ōbject = new Object[3];
  object[0] = selfName;
  object[1] = new Double(x);
  object[2] = new Double(y);
  map.get(userName).invoke("createUser",object);
}
服务端的这个方法就简单啦,只做了一件非常简单的时,就是把当前的传过来的用户信息,转发一个指定的用户就行了(其实就是最新登陆的那个用 户) map.get(userName).invoke("createUser",object),呼叫了客户端的createUser方法。客户 端:
//更新已经登陆的用户
mync.createUser = function(userName,x,y)
{
//根据名字复制一个新的mc
var mc:MovieClip = _root.attachMovie("personMC","personMC" + userName,depth++);
userSet.put(userName,mc);
mc.setName(userName);
mc._x = x;
mc._y = y;
}


客户端的这个方法也很简单,就是根据服务端转发过来的信息,生成一个新的mc(玩家),同时还有位置。
好啦,到了,一个用户登陆到服务器,在所有客户端同步显示的步骤就完成啦。
接下来就把最后的功能完成,就是当一个客户端移动时,在其他客户端的也做对应的动作。呵~~不用我说什么也应该想了吧。恩,其实也是很简单,就是当前用户 操作时,做自己的操作命令通过服务端转发其他所有的客户端,就达到同步的目的了。例如这一句代码:if( Key.isDown( Key.UP ) )
{
  personMC.up();
  mync.call("userAction",null,userName,"up");
}

当按下键盘的UP时,客户端就呼叫服务起的userAction的方法,同时把自己的名字(才知道哪个用户的动作)和动作(具体哪个动作)传给了服务器,下面是服务端的代码

程序代码 程序代码

public void userAction(String userName, String action)
{
  Object[] ōbject = new Object[2];
  object[0] = userName;
  object[1] = action;
  //对所有的用户进行遍历
  if(map.size() > 1)
  {
   //取出当前用户的conn
   IServiceCapableConnection temp = getCurrentConn();
   for(Iterator<IServiceCapableConnection> it = map.values().iterator(); it.hasNext();)
   {
    IServiceCapableConnection iconn = it.next();
    //如果不是当前用户,则发送信息
    if(temp != iconn)
    {
     iconn.invoke("userAction", object);
    }
   }
  }
}


恩,这个方法也简单,就是把收到的信息发送给所有的客户端,注意这句代码
IServiceCapableConnection iconn = it.next();
    //如果不是当前用户,则发送信息
    if(temp != iconn)
    {
     iconn.invoke("userAction", object);
    }
这是为了防止把该信息发送给自己,因为服务器是做遍历的,也就是说会对所有登陆该服务器的用户进行遍历,所以应该排除发送该命令的用户。接下来就看客户端的userAction方法啦
//当其他用户有动作时,被服务器呼叫
mync.userAction = function(name:String,action:String)
{
var mc:MovieClip = userSet.get(name);
//执行对应的方法
mc[action]();
};
它也只做了一件事,就是找出该用户对应的mc(玩家),然后执行相应的动作
OK~~~代码解释就到这里啦。光看我写的这个还是不行,大家还有把源代码提供去看看,大家有什么问题,就到red5的专有论坛去讨论吧。 www.openred5.com. 这个网站。那里也有red5的配置教什么的。这里所以这里我就不在讲述啦。呵~本来想弄些漂亮的人物进去。不过没什 么时间啦,今天是牺牲中午睡眠时间写的,大概花了一个小时,呵呵。如果很多人喜欢的话,往后会继续发布该系列的东西,例如用户说话,攻击,自己的名字是红 色的等等,呵呵。

下载文件 Red5源代码下载

AS3与Red5之间的参数传递

2008年2月20日,14:30:47 | sujun10@21cn.com(弃天笑) 转到全文
差不大一年没去动red5了,现在因为项目需要又开始使用red5,呵呵,先写些基本应用吧
参数传递是最基本的,之前是as2,现在用as3与red5 0.63了,几乎没什么变化.不过flash这边的可以传递的参数也就多了一些.就基本的是
String,int,Number,Boolean,Array,对应到red5这边是String,int,double,boolean,List
下面看基本的代码吧:
Flash:
程序代码 程序代码

/**
 * @(#)ParamRed5.as
 * @author soda.C
 * @version  1.0
 * <br>Copyright (C), 2007 soda.C
 * <br>This program is protected by copyright laws.
 * <br>Program Name:GameHall
 * @data 2008-2-19
 */
package org.sujun.red5.test 
{
    import flash.display.Sprite;
    import flash.net.NetConnection;
    import flash.events.NetStatusEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.Responder;
    /**
     * 测试flash与red5之间参数的传递
     */
    public class ParamRed5 extends Sprite 
    {
        private var netConnection:NetConnection;
        
        public function ParamRed5():void
        {
            netConnection    = new NetConnection();
            
            netConnection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
            netConnection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
            
           netConnection.connect("rtmp://localhost/paramtest");

        }
        
         private function netStatusHandler(event:NetStatusEvent):void 
         {
             trace("连接状态:" + event.info["code"]);
             
            switch (event.info["code"]) 
            {
                case "NetConnection.Connect.Success":
                    trace("连接成功.....");
                    //呼叫服务器的baseParam方法,传递基本参数,string,int,number,Boolean
                    netConnection.call("baseParam", new Responder(baseParamResult),"soda.C",24,1000.1,false);
                    //封装数组,int
                    var ary:Array = new Array();
                    ary.push(1);
                    ary.push(2);
                    ary.push(3);
                    //封装数组,String
                    var ary1:Array = new Array();
                    ary1.push("a");
                    ary1.push("b");
                    ary1.push("c");
                    netConnection.call("receiveArray", new Responder(baseParamResult),ary,ary1);
                    break;
                case "NetStream.Play.StreamNotFound":
                    trace("Stream not found: ");
                    break;
            }
        }
        
        private function baseParamResult(obj:Object):void
        {
            trace(obj);
            trace("响应了.....");
        }
        private function securityErrorHandler(event:SecurityErrorEvent):void 
        {
            trace("securityErrorHandler: " + event);
        }
    }
}

接下来看java代码
ParamRed5App.java,该类继承了ApplicationAdapt
程序代码 程序代码

package org.sujun.red5.test;

import java.util.List;

import org.red5.server.adapter.ApplicationAdapter;

/**
 * 存放被flash客户端调用的方法
 */
public class ParamRed5App extends ApplicationAdapter
{
    public ParamRed5App()
    {
        System.out.println("被初始化了......");
    }
    /**
     * 接受服务器传过来的基本参数
     */
    public void baseParam(String name, int age, double value, boolean flag)
    {
        System.out.println("----name----" + name);
        System.out.println("----age----" + age);
        System.out.println("----value----" + value);
        System.out.println("----flag----" + flag);
    }
    /**
     * 接受客户端传递过来的数组
     */
    public void receiveArray(List<Integer> intArray, List<String> strArray)
    {
        for(int i = 0; i < intArray.size(); i++)
        {
            System.out.println("----intArray----" + intArray.get(i).intValue());
        }
        for(int i = 0; i < intArray.size(); i++)
        {
            System.out.println("----strArray----" + strArray.get(i));
        }
    }
}

代码很简单.......直接复制过去,建立一个red5应用就可以使用了
看结果...


不过,还是我是传上源代码
下载文件 点击下载此源代码
posted @ 2008-06-20 21:00 winnerlan 阅读(390) | 评论 (0)编辑   编辑
http://www.javaeye.com/topic/148292
很多应用都需要将后台发生的变化,实时传送到客户端,而无须客户端不停地刷新、发送请求。本文首先介绍、比较了常用的“ 服务器 推”方案,着重介绍了 Comet - 使用 HTTP 长连接、无须浏览器安装插件的两种“ 服务器 推”方案:基于 AJAX 的长轮询方式;基于 iframe 及 htmlfile 的流方式。最后分析了开发 Comet 应用需要注意的一些问题,以及如何借助开源的 Comet 框架-pushlet 构建自己的“ 服务器 推”应用。

将“ 服务器 推”应用在 Web 程序中,首先考虑的是如何在功能有限的浏览器端接收、处理信息:

1. 客户端如何接收、处理信息,是否需要使用套接口或是使用远程调用。客户端呈现给用户的是 HTML 页面还是 Java applet 或 Flash 窗口。如果使用套接口和远程调用,怎么和 JavaScript 结合修改 HTML 的显示。
2. 客户与 服务器 端通信的信息格式,采取怎样的出错处理机制。
3. 客户端是否需要支持不同类型的浏览器如 IE、Firefox,是否需要同时支持 Windows 和 Linux 平台。

基于客户端套接口的“服务器 推”技术

Flash XMLSocket

这种方案实现的基础是:

1. Flash 提供了 XMLSocket 类。
2. JavaScript 和 Flash 的紧密结合:在 JavaScript 可以直接调用 Flash 程序提供的接口。

具体实现方法:在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序。JavaScript 通过调用此 Flash 程序提供的套接口接口与 服务器 端的套接口进行通信。JavaScript 在收到 服务器 端以 XML 格式传送的信息后可以很容易地控制 HTML 页面的内容显示。

Javascript 与 Flash 的紧密结合,极大增强了客户端的处理能力。从 Flash 播放器 V7.0.19 开始,已经取消了 XMLSocket 的端口必须大于 1023 的限制。Linux 平台也支持 Flash XMLSocket 方案。但此方案的缺点在于:

1. 客户端必须安装 Flash 播放器;
2. 因为 XMLSocket 没有 HTTP 隧道功能,XMLSocket 类不能自动穿过防火墙;
3. 因为是使用套接口,需要设置一个通信端口,防火墙、代理 服务器 也可能对非 HTTP 通道端口进行限制;

不过这种方案在一些网络聊天室,网络互动游戏中已得到广泛使用。

Java Applet 套接口


在客户端使用 Java Applet,通过 java.net.Socket 或 java.net.DatagramSocket 或 java.net.MulticastSocket 建立与 服务器 端的套接口连接,从而实现“ 服务器 推”。

这种方案最大的不足在于 Java applet 在收到 服务器 端返回的信息后,无法通过 JavaScript 去更新 HTML 页面的内容。


基于 HTTP 长连接的“服务器 推”技术

Comet 简介
下面将介绍两种 Comet 应用的实现模型。
基于 AJAX 的长轮询(long-polling)方式
AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP 请求,JavaScript 响应处理函数根据 服务器 返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现“ 服务器 推”与传统的 AJAX 应用不同之处在于:

1. 服务器 端会阻塞请求直到有数据传递或超时才返回。
2. 客户端 JavaScript 响应处理函数会在处理完 服务器 返回的信息后,再次发出请求,重新建立连接。
3. 当客户端处理接收的数据、重新建立连接时, 服务器 端可能有新的数据到达;这些信息会被 服务器 端保存直到客户端重新建立连接,客户端会一次把当前 服务器 端所有的信息取回。
因为这种方案基于 AJAX,具有以下一些优点:请求异步发出;无须安装插件;IE、Mozilla FireFox 都支持 AJAX。
Mozilla Firefox 提供了对 Streaming AJAX 的支持, 即 readystate 为 3 时(数据仍在传输中),客户端可以读取数据,从而无须关闭连接,就能读取处理 服务器 端返回的信息。IE 在 readystate 为 3 时,不能读取 服务器 返回的数据,目前 IE 不支持基于 Streaming AJAX。


基于 Iframe 及 htmlfile 的流(streaming)方式
通过在 HTML 页面里嵌入一个隐蔵帧,然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求, 服务器 端就能源源不断地往客户端输入数据。
每次数据传送不会关闭连接,连接只会在通信出现错误时,或是连接重建时关闭(一些防火墙常被设置为丢弃过长的连接, 服务器 端可以设置一个超时时间, 超时后 通知 客户端重新建立连接,并关闭原来的连接)。
用 iframe 请求一个长连接有一个很明显的不足之处:IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。Google 的天才们使用一个称为“htmlfile”的 ActiveX 解决了在 IE 中的加载显示问题,并将这种方法用到了 gmail+gtalk 产品中。


使用 Comet 模型开发自己的应用

上面介绍了两种基于 HTTP 长连接的“ 服务器 推”架构,更多描述了客户端处理长连接的技术。对于一个实际的应用而言,系统的稳定性和性能是非常重要的。将 HTTP 长连接用于实际应用,很多细节需要考虑。

不要在同一客户端同时使用超过两个的 HTTP 长连接
HTTP 1.1 规范中规定,客户端不应该与 服务器 端建立超过两个的 HTTP 连接, 新的连接会被阻塞。

服务器 端的性能和可扩展性
但是 AJAX 的应用使请求的出现变得频繁,而 Comet 则会长时间占用一个连接,上述的 服务器 模型会变得非常低效,甚至可能会阻塞新的连接。

控制信息与数据信息使用不同的 HTTP 连接
使用长连接时,存在一个很常见的场景:客户端网页需要关闭,而 服务器 端还处在读取数据的堵塞状态,客户端需要及时 通知 服务器 端关闭数据连接。 服务器 在收到关闭请求后首先要从读取数据的阻塞状态唤醒,然后释放为这个客户端分配的资源,再关闭连接。
所以在设计上,我们需要使客户端的控制请求和数据请求使用不同的 HTTP 连接,才能使控制请求不会被阻塞。
在客户和服务器 之间保持“心跳”信息
在浏览器与 服务器 之间维持一个长连接会为通信带来一些不确定性:因为数据传输是随机的,客户端不知道何时 服务器 才有数据传送。 服务器 端需要确保当客户端不再工作时,释放为这个客户端分配的资源,防止内存泄漏。因此需要一种机制使双方知道大家都在正常运行。在实现上:
1. 服务器 端在阻塞读时会设置一个时限,超时后调用会返回,同时发给客户端没有新数据到达的心跳信息。
2. 经过某个时限没有收到客户端的再次请求,会认为客户端不能正常工作,会释放资源。
3. 当 服务器 出现异常,需要 通知 客户端,同时释放资源。

Pushlet - 开源 Comet 框架
Pushlet 是一个开源的 Comet 框架,在设计上有很多值得借鉴的地方,对于开发轻量级的 Comet 应用很有参考价值。

观察者模型
Pushlet 使用了观察者模型:客户端发送请求,订阅感兴趣的事件; 服务器 端为每个客户端分配一个会话 ID 作为标记,事件源会把新产生的事件以多播的方式发送到订阅者的事件队列里。

客户端 JavaScript 库

pushlet 提供了基于 AJAX 的 JavaScript 库文件用于实现长轮询方式的“ 服务器 推”;还提供了基于 iframe 的 JavaScript 库文件用于实现流方式的“ 服务器 推”。
posted @ 2008-06-20 20:58 winnerlan 阅读(708) | 评论 (2)编辑   编辑
类似于QQ游戏百万人同时在线的服务器架构实现
QQ游戏于前几日终于突破了百万人同时在线的关口,向着更为远大的目标迈进,这让其它众多传统的棋牌休闲游戏平台黯然失色,相比之下,联众似乎已经根本不 是QQ的对手,因为QQ除了这100万的游戏在线人数外,它还拥有3亿多的注册量(当然很多是重复注册的)以及QQ聊天软件900万的同时在线率,我们已 经可以预见未来由QQ构建起来的强大棋牌休闲游戏帝国。
  那么,在技术上,QQ游戏到底是如何实现百万人同时在线并保持游戏高效率的呢?
  事实上,针对于任何单一的网络服务器程序,其可承受的同时连接数目是有理论峰值的,通过C++中对TSocket的定义类型:word,我们可以判定 这个连接理论峰值是65535,也就是说,你的单个服务器程序,最多可以承受6万多的用户同时连接。但是,在实际应用中,能达到一万人的同时连接并能保证 正常的数据交换已经是很不容易了,通常这个值都在2000到5000之间,据说QQ的单台服务器同时连接数目也就是在这个值这间。
  如果要实现2000到5000用户的单服务器同时在线,是不难的。在windows下,比较成熟的技术是采用IOCP--完成端口。与完成端口相关的 资料在网上和CSDN论坛里有很多,感兴趣的朋友可以自己搜索一下。只要运用得当,一个完成端口服务器是完全可以达到2K到5K的同时在线量的。但,5K 这样的数值离百万这样的数值实在相差太大了,所以,百万人的同时在线是单台服务器肯定无法实现的。
  要实现百万人同时在线,首先要实现一个比较完善的完成端口服务器模型,这个模型要求至少可以承载2K到5K的同时在线率(当然,如果你MONEY多, 你也可以只开发出最多允许100人在线的服务器)。在构建好了基本的完成端口服务器之后,就是有关服务器组的架构设计了。之所以说这是一个服务器组,是因 为它绝不仅仅只是一台服务器,也绝不仅仅是只有一种类型的服务器。
  简单地说,实现百万人同时在线的服务器模型应该是:登陆服务器+大厅服务器+房间服务器。当然,也可以是其它的模型,但其基本的思想是一样的。下面,我将逐一介绍这三类服务器的各自作用。
  登陆服务器:一般情况下,我们会向玩家开放若干个公开的登陆服务器,就如QQ登陆时让你选择的从哪个QQ游戏服务器登陆一样,QQ登陆时让玩家选择的 六个服务器入口实际上就是登陆服务器。登陆服务器主要完成负载平衡的作用。详细点说就是,在登陆服务器的背后,有N个大厅服务器,登陆服务器只是用于为当 前的客户端连接选择其下一步应该连接到哪个大厅服务器,当登陆服务器为当前的客户端连接选择了一个合适的大厅服务器后,客户端开始根据登陆服务器提供的信 息连接到相应的大厅上去,同时客户端断开与登陆服务器的连接,为其他玩家客户端连接登陆服务器腾出套接字资源。在设计登陆服务器时,至少应该有以下功 能:N个大厅服务器的每一个大厅服务器都要与所有的登陆服务器保持连接,并实时地把本大厅服务器当前的同时在线人数通知给各个登陆服务器,这其中包括:用 户进入时的同时在线人数增加信息以及用户退出时的同时在线人数减少信息。这里的各个大厅服务器同时在线人数信息就是登陆服务器为客户端选择某个大厅让其登 陆的依据。举例来说,玩家A通过登陆服务器1连接到登陆服务器,登陆服务器开始为当前玩家在众多的大厅服务器中根据哪一个大厅服务器人数比较少来选择一个 大厅,同时把这个大厅的连接IP和端口发给客户端,客户端收到这个IP和端口信息后,根据这个信息连接到此大厅,同时,客户端断开与登陆服务器之间的连 接,这便是用户登陆过程中,在登陆服务器这一块的处理流程。
  大厅服务器:大厅服务器,是普通玩家看不到的服务器,它的连接IP和端口信息是登陆服务器通知给客户端的。也就是说,在QQ游戏的本地文件中,具体的 大厅服务器连接IP和端口信息是没有保存的。大厅服务器的主要作用是向玩家发送游戏房间列表信息,这些信息包括:每个游戏房间的类型,名称,在线人数,连 接地址以及其它如游戏帮助文件URL的信息。从界面上看的话,大厅服务器就是我们输入用户名和密码并校验通过后进入的游戏房间列表界面。大厅服务器,主要 有以下功能:一是向当前玩家广播各个游戏房间在线人数信息;二是提供游戏的版本以及下载地址信息;三是提供各个游戏房间服务器的连接IP和端口信息;四是 提供游戏帮助的URL信息;五是提供其它游戏辅助功能。但在这众多的功能中,有一点是最为核心的,即:为玩家提供进入具体的游戏房间的通道,让玩家顺利进 入其欲进入的游戏房间。玩家根据各个游戏房间在线人数,判定自己进入哪一个房间,然后双击服务器列表中的某个游戏房间后玩家开始进入游戏房间服务器。
  游戏房间服务器:游戏房间服务器,具体地说就是如“斗地主1”,“斗地主2”这样的游戏房间。游戏房间服务器才是具体的负责执行游戏相关逻辑的服务 器。这样的游戏逻辑分为两大类:一类是通用的游戏房间逻辑,如:进入房间,离开房间,进入桌子,离开桌子以及在房间内说话等;第二类是游戏桌子逻辑,这个 就是各种不同类型游戏的主要区别之处了,比如斗地主中的叫地主或不叫地主的逻辑等,当然,游戏桌子逻辑里也包括有通用的各个游戏里都存在的游戏逻辑,比如 在桌子内说话等。总之,游戏房间服务器才是真正负责执行游戏具体逻辑的服务器。
  这里提到的三类服务器,我均采用的是完成端口模型,每个服务器最多连接数目是5000人,但是,我在游戏房间服务器上作了逻辑层的限定,最多只允许 300人同时在线。其他两个服务器仍然允许最多5000人的同时在线。如果按照这样的结构来设计,那么要实现百万人的同时在线就应该是这样:首先是大 厅,1000000/5000=200。也就是说,至少要200台大厅服务器,但通常情况下,考虑到实际使用时服务器的处理能力和负载情况,应该至少准备 250台左右的大厅服务器程序。另外,具体的各种类型的游戏房间服务器需要多少,就要根据当前玩各种类型游戏的玩家数目分别计算了,比如斗地主最多是十万 人同时在线,每台服务器最多允许300人同时在线,那么需要的斗地主服务器数目就应该不少于:100000/300=333,准备得充分一点,就要准备 350台斗地主服务器。
  除正常的玩家连接外,还要考虑到:
  对于登陆服务器,会有250台大厅服务器连接到每个登陆服务器上,这是始终都要保持的连接;
  而对于大厅服务器而言,如果仅仅有斗地主这一类的服务器,就要有350多个连接与各个大厅服务器始终保持着。所以从这一点看,我的结构在某些方面还存在着需要改进的地方,但核心思想是:尽快地提供用户登陆的速度,尽可能方便地让玩家进入游戏中。
posted @ 2008-06-20 20:57 winnerlan 阅读(96) | 评论 (0)编辑   编辑
基于 WEB 的实时事件通知方式大致有五种方案:HTTP拉取方式(pull),HTTP流,Long Polling,FlashXMLSocket方式,Java Applet。


首先说下Comet这个词,Comet 这个词是最早由Alex Russell(DojoToolkit 的项目 Lead)提出的,称基于 HTTP 长连接、无须在浏览器端安装插件的“服务器推(Push)”技术为“Comet”。


1.HTTP拉取方式(pull)
在这种传统的方法中,客户端以用户可定义的时间间隔去检查服务器上的最新数据。这种拉取方式的频率要足够高才能保证很高的数据精确度,但高频率可能会导致 多余的检查,从而导致较高的网络流量。而另一方面,低频率则会导致错过更新的数据。理想地,拉取的时间间隔应该等于服务器状态改变的速度。常见的实现如利 用 " <meta http-equiv="refresh" c />" tag,当然利用xmlHttpRequest定时取也是一种方法。


2.HTTP流(Push机制)
HTTP流有两种形式:
* Page Stream: 页面上不间断的HTTP连接响应(HTTP 1.1Keep Alive).
  通过在 HTML 页面里嵌入一个隐蔵帧(iframe),然后将这个隐蔵帧的 SRC属性设为对一个长连接的请求,服务器端就能源源不断地往客户端输入数据。
* Service Stream:XMLHttpRequest连接中的服务器数据流。
  客户端是在 XMLHttpRequest 的 readystate 为4(即数据传输结束)时调用回调函数,进行信息处理。当 readystate 为 4 时,数据传输结束,连接已经关闭。Mozilla Firefox 提供了对Streaming AJAX 的支持,即 readystate 为 3时(数据仍在传输中),客户端可以读取数据,从而无须关闭连接,就能读取处理服务器端返回的信息。IE 在 readystate 为 3时,不能读取服务器返回的数据,目前 IE 不支持基于 Streaming AJAX。

注:使用 Page Stream(iframe) 请求一个长连接有一个很明显的不足之处:IE、Morzilla Firefox下端的进度栏都会显示加载没有完成,而且 IE 上方的图标会不停的转动,表示加载正在进行。Google 的天才们使用一个称为“htmlfile”的 ActiveX解决了在 IE 中的加载显示问题,并将这种方法用到了 gmail+gtalk 产品中。Alex Russell 在 “What else is burrieddown in the depth's of Google's amazing JavaScript?”文章中介绍了这种方法。Zeitoun 网站提供的comet-iframe.tar.gz,封装了一个基于 iframe 和 htmlfile 的 JavaScript comet 对象,支持IE、Mozilla Firefox 浏览器,可以作为参考。(http://alex.dojotoolkit.org/?p=538)

3.长时间轮询(Long Polling)
也就是所谓的异步轮询(AsynchronousPolling),这种方式是纯服务器端推送方式和客户端拉取方式的混合。它是基于BAYEUX协议 (http://svn.xantus.org/shortbus/trunk/bayeux/bayeux.html) 的。这个协议遵循基于主题的发布——订阅机制。在订阅了某个频道后,客户端和服务器间的连接会保持打开状态,并保持一段事先定义好的时间(默认为45 秒)。如果服务器端没有事件发生,而发生了超时,服务器端就会请求客户端进行异步重新连接。如果有事件发生,服务器端会发送数据到客户端,然后客户端重新 连接。
  1.  服务器端会阻塞请求直到有数据传递或超时才返回。
  2. 客户端 JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
  3.当客户端处理接收的数据、重新建立连接时,服务器端可能有新的数据到达;这些信息会被服务器端保存直到客户端重新建立连接,客户端会一次把当前服务器端所有的信息取回。


4.Flash XMLSocket(push机制)


这种方案实现的基础是:
  1. 安装了 Flash 播放器,Flash 提供了 XMLSocket 类(Flash 7.0.14以上版本)。
  2.JavaScript 和 Flash 的紧密结合:在 JavaScript 可以直接调用 Flash 程序提供的接口。

具体实现方法:在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序。JavaScript 通过调用此 Flash程序提供的套接口接口与服务器端的套接口进行通信。JavaScript 在收到服务器端以 XML 格式传送的信息后可以很容易地控制 HTML页面的内容显示。

关于如何去构建 JavaScript 与 Flash XMLSocket 的 Flash 程序,以及如何在 JavaScript 里调用Flash 提供的接口,我们可以参考 AFLAX(Asynchronous Flash and XML)项目提供的 Socket Demo 以及SocketJS(请参见 [http://www.aflax.org/ Asynchronous Flash and XML,提供了强大的Flash、Javascript 库和很多范例。])。

Javascript 与 Flash 的紧密结合,极大增强了客户端的处理能力。从 Flash 播放器 V7.0.19 开始,已经取消了XMLSocket 的端口必须大于 1023 的限制。Linux 平台也支持 Flash XMLSocket 方案。但此方案的缺点在于:
  1. 客户端必须安装 Flash 播放器;
  2. 因为 XMLSocket 没有 HTTP 隧道功能,XMLSocket类不能自动穿过防火墙;
  3. 因为是使用Socket接口,需要设置一个通信端口,防火墙、代理服务器也可能对非 HTTP通道端口进行限制;
  4. 必须使用XML格式作为消息格式,数据冗余增大。

此方案在一些网络聊天室,网络互动游戏中得到广泛使用。

5. Java Applet(Push机制)
类似于Flash XMLSocket方式。目前已经很少使用,原因极可能是因在手机等移动终端缺少支持。


总结和建议:

如果我们想要高数据一致性和高网络性能,我们就应该选择推送方式。但是,推送会带来一些扩展性问题;服务器应用程序CPU使用率是拉取方式的7倍。根据 TUD(http://swerl.tudelft.nl/twiki/pub/Main/TechnicalReports/TUD-SERG- 2007-016.pdf)的测试结果,服务器性能会在350-500个用户时趋于饱和。对于更大数量的用户,服务器端需要维护大量并发的长连接。在这种 应用背景下,服务器端需要考虑负载均衡和集群技术;或是在服务器端为长连接作一些改进。

使用拉取方式,要想达到完整的数据一致性以及很高的网络性能是很困难的。如果拉取的时间间隔大于数据更新的时间间隔,就会发生一些数据的遗失。而如果小于 数据更新的时间间隔,网络性能就会受到影响。拉取方式只有在拉取时间间隔等同于数据更新时间间隔时,才会恰到好处。但是,为了达到那样的目标,我们就需要 提前知道准确的数据更新时间间隔。然而,数据更新的时间间隔很少是静态不变并可以预知的。这使得拉取方式只有在数据是根据某种特定模式发布的情况才有用。

控制信息与数据信息使用不同的 HTTP 连接

使用长连接时,存在一个很常见的场景:客户端网页需要关闭,而服务器端还处在读取数据的堵塞状态,客户端需要及时通知服务器端关闭数据连接。服务 器在收到关闭请求后首先要从读取数据的阻塞状态唤醒,然后释放为这个客户端分配的资源,再关闭连接。所以在设计上,我们需要使客户端的控制请求和数据请求 使用不同的 HTTP 连接,才能使控制请求不会被阻塞。

在实现上,如果是基于 iframe 流方式的长连接,客户端页面需要使用两个iframe,一个是控制帧,用于往服务器端发送控制请求,控制请求能很快收到响应,不会被堵塞;一个是显示帧, 用于往服务器端发送长连接请求。如果是基于 AJAX的长轮询方式,客户端可以异步地发出一个 XMLHttpRequest 请求,通知服务器端关闭数据连接。


在客户和服务器之间保持“心跳”信息
在浏览器与服务器之间维持一个长连接会为通信带来一些不确定性:因为数据传输是随机的,客户端不知道何时服务器才有数据传送。服务器端需要确保当客户端不再工作时,释放为这个客户端分配的资源,防止内存泄漏。因此需要一种机制使双方知道大家都在正常运行。在实现上:
  1.服务器端在阻塞读时会设置一个时限,超时后阻塞读调用会返回,同时发给客户端没有新数据到达的心跳信息。此时如果客户端已经关闭,服务器往通道写数据会出现异常,服务器端就会及时释放为这个客户端分配的资源。
  2. 如果客户端使用的是基于 AJAX的长轮询方式;服务器端返回数据、关闭连接后,经过某个时限没有收到客户端的再次请求,会认为客户端不能正常工作,会释放为这个客户端分配、维护的资源。
  3. 当服务器处理信息出现异常情况,需要发送错误信息通知客户端,同时释放资源、关闭连接。
posted @ 2008-06-20 20:56 winnerlan 阅读(109) | 评论 (0)编辑   编辑

你需要用Ajax技术,你可以在网上查相关资料

我明白你的意思,你需要服务器端发出通知,而不是用客户端提出请求。

我告诉你以前我是怎么做的吧,你到网上查一个jetty的jsp平台,我以前用的是jetty6.0 ,现在应该有更高版本了,jetty6.0提供了一个叫做continuous connection的东西,这是什么呢,简单解释一下,就是一种对服务器端收到http请求,但是不马上返回结果,一直挂起,知道服务器发生变化,觉得 应该通知客户端的时候,对客户端返回请求。

换句话说,只要你的客户端一开始向你服务器端请求数据,服务器端挂起请求,在有需要时再返回客户端,客户端使用Ajax异步处理,在回调函数里写上相关处 理,服务器端没有返回时回调函数不会被触发。这样就能做到即时的消息通知,而且并不像客户端不断refresh一样占用无效的流量。

jetty6.0里面提供了一个chat的sample,这个技术就是为聊天室设计的。

关于连接数的问题,其实65534是一个错误的数字,首先,如果你用的是windows平台的服务器,很遗憾,上限2048,因为以前我也做过相关东西,所以也找过破解,没有办法改变这个连接数。

然后,如果你愿意用其他平台,那么可以告诉你Solaris10的连接数达到十万,我有个朋友就是使用jetty6.0和solaris10的组合做的聊天室,通讯效果不错的。

客户端执行简单的js脚本,就是IE,firefox这样的浏览器。
如果客户端发出请求后,服务器马上回复,还有后面的事情么?

资料你上他们官方网站看好了,这只是Ajax技术应用的一个极端而已。

我这里假设你了解Ajax技术吧,不了解可以上网查。

客户端里的js代码使用Ajax通过XmlHttpRequest请求服务器端,服务器端不返回,形成长连接,等服务器端有数据更新,服务器返回,Ajax异步处理,这样子通信就是即时的。

jetty官方网站:
http://www.mortbay.org/
你到上面下一个jetty下来,里面有chat这个sample的。

solaris10支持10万连接数,这个我就不解释了,你到sun公司里面看他的产品介绍就知道了,可以免费下载的。

我这里也没什么资料,开发文档涉及商业机密,不可能给你的。只是给你一个提示而已,另外也纠正你一个误区,就是连接数的问题,你服务器端为什么要对每一个请求新开一个端口呢?难道就不能所有连接共用一个端口?(比如80端口)

我现在又不知道你基础怎样,所以也只能说到这里吧。我觉得你只是需要一种即时通信的思路了,什么服务器作为后台不重要。如果你技术过关,自己写一个web服务器处理这种并发请求也是可以的,不一定要用jetty(使用jetty需要JAVA基础)

另外,这个思路最关键的地方就是要挂起客户端的请求,延时返回。

只要你不使用activeX,或者java applet的方式,只是轻量级的http瘦客户端,你就必须明白,你必须遵循request-respond的机制,这个是不可能改变的,不管你是 xmlhttprequest还是不断refresh,你只能在这个机制上加以修改,让他达到服务器通知的效果。

延时返回的方法有很多,大部分http服务器都可以提供,比如php里面可以sleep,我之所以使用jetty是因为它是很底层的,可以很底层的管理每 一个请求的线程,而且专门设计过一个挂起请求的解决方案和专用的类,这样的好处就是挂起客户端请求的开销可以降到最小,用php或者.net开销都过大 了,因为他们没有为此专门设计过。

太感谢了,我确实没这方面的基础,现在先看看可行性,以后开发里,还得多多请教。5天后给你加分哈
另外,如你所说,jetty是性能最好的,但我想问下,如果我想做到商用的产品里去的话,用jetty合适吗?
或者用什么方案最适合?
jetty的部署你可以在他的官方网站上看看,可以很好的部署,甚至可以加到对方系统服务里面。

另外,我的方案只是在轻量级瘦客户端开发上提出的,这个前提是你提供服务器,对方只提供客户端。你想做成商品,你的客户想要看到的展示很可能是眩目的效果,js不是不能写很漂亮的效果,可是js的表现力再强也强不过flash,哪怕你用过所有的css滤镜。

而flash里面有socket编程,带flash的页面属于富客户端,它有天然的socket长连接,不需要你改装http的request-response机制。你只需要再写一个socket服务器段来监听请求,建立连接,就能向客户端发出通知了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值