服务器端回调有啥用呢?
比如:
向服务器上传了一个文件,但是,为了节约空间或出于其他目的,服务器要对刚上传的文件进行处理(压缩或者多媒体文件转码),这些操作无法马上向客户端回复,而客户端也不可能就停在这里一直在等。我们希望,在客户端上传文件后马上返回,而服务器对文件处理完成后再通知一下客户端。
这样就引出一个东东——回调,E文叫Call Back。我估计用E文表述可能更好理解,Call back就是相对于Call to而言的,即调用的方向与Call to相反。
在WCF中使用回调,只需要多定义一个接口即可,这个接口的方法和服务协定一样,要附加OperationContractAttribute特性。
然后在定义服务协定时,在ServiceContractAttribute的CallbackContract中设置一个回调接口的Type。
在服务操作中,通过OperationContext的GetCallbackChannel方法取出回调协定的实例,调用回调的方法,就会在客户端寻找回调接口的实现类并调用对应的成员。
下面我们做一个摇号程序进行说明
一、服务端
/// <summary>
/// 1、定义一个回调接口
/// </summary>
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void CallClient(int value);
}
/// <summary>
/// 2、定义服务协定
/// </summary>
[ServiceContract(Namespace="MyNamespace",
CallbackContract=typeof(ICallback),//标注回调协定
SessionMode=SessionMode.Required //要求会话
)]
public interface IService
{
/// <summary>
/// 会话从调用该操作开始
/// </summary>
[OperationContract(IsOneWay=true,IsInitiating=true,IsTerminating=false)]
void CallServerOperation();
/// <summary>
/// 调用该操作后,会话结束
/// </summary>
[OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true)]
void End();
}
/// <summary>
/// 3、实现服务协定
/// </summary>
public class MyService : IService,IDisposable
{
private ICallback m_cb = null; //回调接口
private System.Threading.Timer m_timer = null; //计时器,定时干活
private Random m_random = null;//随机数
public void CallServerOperation()
{
m_cb = OperationContext.Current.GetCallbackChannel<ICallback>();
m_random = new Random();
//生成随机数,并回调到客户端;每3秒执行一次
m_timer = new System.Threading.Timer((obj) => m_cb.CallClient(m_random.Next()), null, 10, 3000);
}
public void End()
{
Console.WriteLine("会话结束");
}
public void Dispose()
{
m_timer.Dispose();
Console.WriteLine("{0}-服务实例已释放.", DateTime.Now.ToLongTimeString());
}
}
//配置服务器
static void Main()
{
Console.Title = "WCF服务端";
//服务基地址
Uri baseUri = new Uri("http://localhost:3000/Service");
//声明服务器主机
using (ServiceHost host = new ServiceHost(typeof(MyService), baseUri))
{
/*既支持会话传输速度又快的非TCP莫属了,所以这里我选择NetTcpBinding;
* 这样在默认行为下,每启动一个会话就创建一个服务实例,而当会话结束时就会释放。
*/
//添加绑定和终结点
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(IService), binding, "net.tcp://localhost:1211/rr");
//添加服务描述
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
//启动服务
try
{
host.Open();
Console.WriteLine("服务已启动");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadKey();
//关闭服务
host.Close();
}
}
二、客户端
在客户端实现回调接口
/// <summary>
/// 实现回调接口
/// </summary>
public class MyCallback : WS.IServiceCallback
{
/// <summary>
/// 回调引发该事件
/// </summary>
public event EventHandler<int> ValueCallback;
/// <summary>
/// 因为该方法是由服务器调用的
/// 如果希望在客户端能及时作出响应,应当使用事件
/// </summary>
/// <param name="value">value</param>
public void CallClient(int value)
{
if (ValueCallback != null)
{
ValueCallback(this, value);
}
}
}
public partial class Form1 : Form
{
WS.ServiceClient m_sc;
readonly MyCallback m_mcallback;
public Form1()
{
InitializeComponent();
m_mcallback = new MyCallback();
m_mcallback.ValueCallback += mc_ValueCallback;
labNum.Text = "";
btnStop.Enabled = false;
}
void mc_ValueCallback(object sender, int e)
{
labNum.Text = e.ToString();
}
private void btnStart_Click(object sender, EventArgs e)
{
m_sc = new WS.ServiceClient(new System.ServiceModel.InstanceContext(m_mcallback));
m_sc.CallServerOperation();
btnStart.Enabled = false;
btnStop.Enabled = true;
}
private void btnStop_Click(object sender, EventArgs e)
{
m_sc.End();
btnStart.Enabled = true;
btnStop.Enabled = false;
}
}
效果如下: