先把文章中要用到一些扩展程序集贴出来。
C#用到了System.Web.Extensions程序集,包括3个文件:
System.Web.Extensions.dll
System.Web.Extensions.Design.dll
AJAXExtensionsToolbox.dll
Flash用到了adobe的corelib-.90程序集:
corelib.swc
网上现在有很多将flash与C# web 服务程序通讯的资料,不过很多都是用remoting或者web service的方式做的,而且很多高手还做了效率比较,结果是基于TCP协议的remoting效率最好,但是这里对remoting就不多说了,并且基于TCP的remoting因为协议不同效率差别肯定明显。我还是介绍在HTTP协议下的通讯方法吧。
这里使用iis作为服务器,服务程序只要能处理客户端提交的数据,能向客户端返回数据就行(当然push做不到,http协议嘛)。所以处理程序大多直接实现IHttpHandle接口,当然从Page继承是一点也没有问题的,因为Page类也实现了IHttpHandle接口,如何继承Page,如何实现IHttpHandle的接口方法就给个例子吧,这个资料挺多的。
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.SessionState;
namespace com.Flight.Script
{
/// <summary>
/// 脚本控制请求,IRequiresSessionState接口没有啥方法,纯粹为了session支持
/// </summary>
public class ScriptHandler : IHttpHandler, IRequiresSessionState
{
/// <summary>
/// 处理脚本请求
/// </summary>
/// <param name="context">脚本名称</param>
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("输出内容");
}
/// <summary>
/// 是否允许后续请求处理
/// </summary>
public bool IsReusable
{
get
{
return true;
}
}
}
}
这里提一下web.config配置,下面是一个例子,在这里例子里面我修改了扩展名“.do”,这样的配置要求修改iis服务器设置的,如果没有自己的服务器最好别这样做,比较麻烦,可以使用“ashx”或者“axd”都可以。
<httpHandlers>
<add verb="*" path="chat.do" type="com.gs.chat.Chat, Game"/>
</httpHandlers>
下面是服务器上将对象进行JSON序列化的程序,其实JSON序列化就是将一个对象转换成有规律的字符串,能够被别人解读出来,但不是所有的对象都能被序列化,一定要实现了ISerializable接口的类才行,可以参考Hashtable类,这个类就实现了ISerializable接口中的GetObjectData方法,因此这个类型的对象进行反序列化是没有问题的。
using ...
using System.Web.Script.Serialization;
using ...
...
/// <summary>
/// 向客户端返回的数据
/// 默认采用json编码格式
/// </summary>
/// <param name="_result">服务器方法的方会对象</param>
/// <returns>返回字符串</returns>
public virtual string JsonResult(object _result)
{
//返回的字符串
string resultString = "";
//采用了微软的序列化方式
JavaScriptSerializer jss = new JavaScriptSerializer();
resultString = jss.Serialize(_result);
return resultString;
}
/// <summary>
/// 反序列化,从JSON字符串转换到对象
/// </summary>
/// <param name="_jsonstring">JSON串</param>
/// <returns>反序列化得到的对象</returns>
public virtual object JsonObject(string _jsonstring)
{
JavaScriptSerializer jss = new JavaScriptSerializer();
return jss.DeserializeObject(_jsonstring);
}
...
输出都是一样的,直接Write:
context.Response.Write(_text);
这里对于服务器的开发方法可以很多,比如在同一个IHttpHandle中可以写多个处理程序,分别处理不同的请求,然后有个专门的类负责分析要执行哪个方法。下面是发送和接收聊天消息的方法,供参考:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using com.Flight.Web.Ajax;
using System.Text.RegularExpressions;
namespace com.gs.chat
{
public class Chat : AjaxController
{
protected void Page_Load(object sender, EventArgs e)
{
AjaxJsBuilder.RegistType(typeof(Chat));
StartProcess();
}
[SafeMethod("A")]
[AjaxMethod()]
public Hashtable SendMessage(string _msg)
{
string patternForCmd = @"-\|cmd:[a-zA-z]+\|-";
Regex rg = new Regex(patternForCmd, RegexOptions.IgnoreCase | RegexOptions.Singleline);
Match m = rg.Match(_msg);
if (m.Success)
{
string cmd = m.Value;
if (cmd.ToLower().Equals("-|cmd:clear|-"))
{
MessageContainer.MsgContainer.Clear();
}
}
else
{
Message msg = new Message("me", "all", _msg, ToType.ALL);
MessageContainer.MsgContainer.Add(msg);
}
Hashtable rht = new Hashtable();
rht.Add("stat", "OK");
rht.Add("msg", "发送成功。");
return rht;
}
[SafeMethod("A")]
[AjaxMethod()]
public MessageContainer ReceiveMessage(string _u)
{
MessageContainer mc = new MessageContainer();
foreach (Message msg in MessageContainer.MsgContainer)
{
if (msg.From.Equals(_u) || msg.To.Equals(_u))
{
mc.Add(msg);
}
}
return mc;
}
}
}
AjaxController这个类是自己写的,从page继承,用来做一些基本处理,比如JSON序列化啊,反序列化啊,安全检查啊,还有分析客户端要调用的方法之类,后面也会提到一些里面的具体设计。
下面看看客户端的开发,首先是向服务器发送请求,这个比较简单,直接贴一个简单例子,应该都明白
import flash.net.URLLoader;
import flash.net.URLRequest;
var request:URLRequest = new URLRequest(url);
var loader:URLLoader = new URLLoader();
var request_data:String = ajax_flag + "&" + method_flag + method + "&" + parameters;
request.data = request_data;
request.method = URLRequestMethod.POST;
loader.load(request);
主要是在loader的complete事件中处理的时候要注意把服务器返回的字符串进行JSON反序列化,转换成对象,代码如下:
import com.adobe.serialization.json.JSON; //导入使用的到类
在load_complete事件里面写如下代码:
//从事件对象想读取服务器返回的数据,反序列化为对象
var receiveMsg:Object = JSON.decode(event.target.data);
获取receiveMsg对象之后就可以通过点操作符访问属性数据了。以一个HashTable为例,其中的Key就是反序列化后对象的属性,Value就是属性值。
在C#中能够被直接序列化的容器对象很多,可以更具需要选择,如果都不能满足你的要求,也可以自己写一个对象,实现ISerializable接口的GetObjectData方法进行数据传输。
按照上面的实现思路,如果一个IHttpHandle只能处理一种请求那么我们就要写出很多很多的IHttpHandle这样就无法和webservice相提并论了,因为我们知道在webservice中一个service可以提供多个方法响应客户端的请求,这个其实只要我们稍作处理就可以了,比如上面的例子里面我们看到请求字符串中包含了一些特殊的参数:
ajax_flag + "&" + method_flag + method
可以很容易看出来,这里就是用来告诉服务器我要调用哪些方法的参数。
注意:服务器方法在响应请求之前还要做安全判断,这个判断很重要,否则随便就能调用服务器程序那岂不太危险?这里给出一个简单的解决方法,可以采用一个HttpModule做前端判断,如果已经登录,请求参数中没有特殊的符号(防止注入),并且有访问这个HttpHandle的权限那么继续,然后在HttpHandle中给指定的方法加个安全特性代码如下:
[SafeMethod("A")]
[AjaxMethod()]
public MessageContainer ReceiveMessage(string _u)
{
MessageContainer mc = new MessageContainer();
foreach (Message msg in MessageContainer.MsgContainer)
{
if (msg.From.Equals(_u) || msg.To.Equals(_u))
{
mc.Add(msg);
}
}
return mc;
}
[AjaxMethod()]和[SafeMethod("A")]这两个特性说明ReceiveMessage方法是允许客户端调用的,并且用户具有特性A的时候才能执行这个方法,否则报错。