




已阅读5页,还剩2页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
使用 .NET 实现 Ajax 长连接 (Web Service)Ajax的长连接,或者有些人所说的Comet,就是指以XMLHttpRequest的方式连接服务器,连接后服务器并非即时写入相应并返回。服务器会保持连接并等待一个需要通知客户端的事件,该事件发生后马上将数据写入响应,这时候客户端就以相当“实时”的方式接收到事件通知。具体的通信模型,请参考这篇文章:Comet:基于 HTTP 长连接的“服务器推”技术,里面已经说得非常详细了,我就不再复述了。我们接着开始讨论如何使用.NET实现这个模型。首先我们能想到的是,我们需要一个Web Service,可以是ASP.NET Web Service,也可以是WCF Web Service,ASP.NET AJAX Library两者都支持。在这里,为了简单起见,就选择大家更熟悉的ASP.NET Web Service举例。然后,我们写下以下两个函数签名:public void Send(Message message);public Message Wait();其中,Send函数用来发送一个Message对象,而Wait函数用来等待一个Message对象。然后,让我们来讨论一些细节问题。无事件导致超时首先,长期保持连接时不行的。对于服务器和客户端来说,这不是个问题,但我们永远都要记住中间可能存在各式各样配置怪异的网关和代理,它们上面可能有各式各样的超时规则,因此Comet最好设计为定期重连。一般情况下,如果30秒没有任何事件发生,服务器端就应该通知客户端确实没有事件发生,结束掉本次请求,然后重新开始一次新的请求以便继续等待。那么上述函数签名可否用来返回一个无事件的消息呢?这是显然可以的,我们可以选择返回null表示无事件,或者返回一个EmptyMessage常量,这视乎我们使用class还是struct来定义Message。(甚至,我们还可以做一个名为NoMessageMessage的Message派生类来做这个事情。)定义发送目标上述函数签名确实能用来收发消息,但是没指名发给谁。可能有人会说,发送给谁可以在Message类里面通过一个属性来定义啊。但是Wait()方法没有说明接受方是谁,服务器端依然不知道哪些消息应该让你接收。因此,我们引入Channel的概念,Channel使用其名称来标识,相同名称的就必然是同一个Channel。在发送与接受时,通过名称指定要发送到哪个Channel,这样问题就解决了。此时,函数签名修改如下:public void Send(string channelName, Message message);public Message Wait(string channelName);可靠的消息队列想象一个可能发生的情况,服务器端向你发送一个消息,你没有成功接收,但是服务器端认为发送了就成功了,消息从队列删除了,然后这个消息就永久丢失掉了。可能有人会强调TCP多么可靠,服务器端发送的消息如果在TCP的层面发生问题了,肯定会引发Socket级别的Exception,这个Exception冒泡上来,服务器端就能截获,从而得知发送失败,然后先不删除队首消息。可是别忘了,中间是可能存在代理的,如果代理成功把消息收回去了,可是代理发送到客户端这一步失败了,服务器端就不一定会发生异常了。因此,我们需要制定一种策略,来确保下行消息总能发送到客户端。在这里,我们选择了引入逐个ACK的机制,来确认消息的接收。也就是说,服务器端发送给客户端的消息带有一个序号,在客户端收到消息后就将该序号发回给服务器端,已确认它受到了该消息。这时候,函数签名更改如下:public int Send(string channelName, Message message);public Message Wait(string channelName, int sequence);我们使用Wait()接收到的Message中,应该有一个Sequence的属性,标记它的序号。然后,再我们执行下一次Wait()时就将该序号加1的值通过sequence参数传递回去,让服务器知道我们期望下一条消息的编号是这个。例如我们收到Message,其Sequence属性为836,那么下一次调用Wait()的时候就传给服务器837。服务器端此时应该保留了编号为836的Message在对首,如果客户端继续请求836号消息,证明它上次没收到,这次仍然发送836号消息给它;如果客户端请求837号消息,证明它成功收到836号消息的,这次就发送837号消息给它。如果都不是,那该怎么办?那意味着,这是一个错误的请求,甚至可能是攻击请求,因为正常情况下不应该出现这样的请求的,服务器端可以考虑抛个无关紧要的Exception(不要告诉攻击者你知道他在攻击了),甚至直接给个400 (bad request)的响应代号。与Wait()类似的,Send()也可以加入ACK机制,只需要将返回类型从void改为int就可以了,这个值就专门用于传递消息编号,实现方式和Wait()是一样的,不过Send()是由客户端保存待发送消息的队列。小结到此为止。我们的Web Service就写好了。这就写好了?只有签名没有函数体?是的,复杂的工作留给model去做,Web Service在这里只是相当于一个view,用于将model的接口暴露出来。很多人对此就提出了问题,如何hold住请求让它30秒不断开了?这其实很简单,只需要Sleep()一下就可以了:Thread.Sleep(30 * 1000);然而问题是,我们不是要等30秒然后看看是否有事件需要返回,而是在这30秒内随时有事件随时返回。因此,我们需要一套机制来在等待的过程中检查是否有事件发生了。Monitor模型在.NET里面,大家最熟悉的线程同步模型应该就是Monitor模型了。没听说过?就是C#的那个lock关键字,实际上它编译出来就是一对Monitor.Enter()和Monitor.Exit()。通过lock命令,我们可以针对一个对象创建一个临界区,代码执行到临界区入口时必须获取到该对象的锁才能执行下去,并且在临界区的出口释放该锁。然而这种模型不太适用于解决我们的问题,因为我们需要等待一个事件,如果使用lock来等待的话,那就是说要先在Web Service外部把对象锁上,然后等事件触发了就解锁,这时候Web Service才顺利进入临界区域。事实上,要进行这类型的阻塞,还有一个更好的选择,那就是Mutex。Mutex模型Mutex,也就是mutual exclusive的缩写,“互斥”的意思。Mutex是如何运作的?这有点像是银行的排队叫号系统,所有等待服务的人都坐在大厅里等候(wait)被叫,当一个服务窗口空闲时它就会发出一个信号(signal)来通知下一位等候服务的人。总之,所有执行wait指令的线程都在等候,而每一个signal能够让一个线程结束等候继续执行。在.NET里面,wait和signal这两个操作分别对应Mutex.WaitOne()和Mutex.ReleaseMutex()这两个方法。我们可以让Web Service的线程使用Mutex.WaitOne()进入等候状态,而在事件发生时使用Mutex.ReleaseMutex()来通知Web Service线程。因为必须在Mutex.ReleaseMutex()发生后Mutex.WaitOne()才可能继续执行下去,因此能够执行下去就证明必然有事件发生了并且调用了Mutex.ReleaseMutext(),这时候就可以放心地去读取事件消息了。简单示例在选定使用Mutex模型后,我们来编写一个简单的示例。首先,我们要在WebService派生类内定义一个Mutex,还有一个代表消息的字符串。Mutex mutex = new Mutex();string message;然后,我们定义两个WebMethod。为了把问题简单化,我们选用上一篇文章中开头所说的两个函数签名,也就说只能在一个Web Service内自己发自己收,没有发送目标的概念,也没有超时的概念,还没有可靠性设计。同时,我们将Message类型替换为普通字符串,以便于我们测试。我们先编写发送消息的函数:public void Send(string message) this.message = message; this.mutex.ReleaseMutex();在这个发送函数里,首先我们把消息放进了类内全局的变量中,然后让全局的Mutex类释放一个signal。这时候,如果有线程在等待,它可以马上执行下去。如果此时没有线程在等待,那么下一个wait的线程执行到该阻塞的地方就能够不受阻塞继续执行下去。现在我们来编写接收消息的函数:public string Wait() this.mutex.WaitOne(); return this.message;接收函数一开始就进入wait状态。在得到signal后,需要做的事情就是把全局的消息返回给客户端。亲身体验最后,我们可以通过ASP.NET Web Service本身支持的Web测试界面来测试一下我们的代码。我们开两个浏览器窗口,一个进入Send()调用,一个进入Wait()调用。然后我们按照如下方法来测试:1. 首先执行Send(Hello),然后执行Wait()。这时候你可以马上看到Hello。 2. 首先执行Wait(),让它等待返回,这时候执行Send(Hello)。随后你可以看到Wait()那段返回Hello了。 3. 按如下顺序执行:Send(Hello);Wait();Send(World);Wait(); 4. 按如下顺序执行:Send(Hello);Send(World);Wait();Wait(); 5. 按如下顺序执行:Wait();Wait();Send(Hello);Send(World); 6. 按如下顺序执行:Wait();Send(Hello);Wait();Send(World); 你会发现这样一些奇怪的结果:第3个测试返回的是World和World。第5个测试先返回Hello的并不一定是先执行的那个Wait()线程。后者在某些情况下不是什么问题,特别是长连接中一般之后一个Wait()线程在等待中,所以我们可以不管。而前者,则是因为没有消息队列所造成的,我们只有长度为1的消息窗口,所以只能缓存最后一个消息。这个问题我们将在下一篇文章中解决。小结在本文中,我们看到了不同的线程同步模型的差异。Monitor模型的lock本质上是一个Semaphore,也就是一个不能连续sig
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年在线教育平台技术支持服务对用户体验满意度的影响报告
- 2025年城市污水处理厂智能化改造对城市排水设施运行效率的影响报告
- 2025年消费金融用户画像与精准营销在老年金融市场的应用报告
- 开发商购房合同
- 2024-2025学年山西省临汾市古县三年级下学期期中英语试题卷(含答案)
- 制度规章责任制操作规程
- 应力腐蚀破裂课件
- 巡前培训保密培训课件
- 岩石力学课件蔚宝华
- 尾矿库安全教育培训课件
- 肉制品工艺学-香肠类制品-课件
- 敬畏规则行有所止生命教育主题班会
- 哮病(支气管哮喘急性发作)中医护理方案
- 中小企业员工离职原因分析与对策研究
- GB/T 9728-2007化学试剂硫酸盐测定通用方法
- GB/T 2992.1-2011耐火砖形状尺寸第1部分:通用砖
- 神经系统的分级调节课件 【知识精讲+备课精研+高效课堂】 高二上学期生物人教版选择性必修1
- 中医门诊消毒隔离制度
- 三年级上册数学试卷-第一单元 混合运算 北师大版 (含答案)
- 教学课件-英语学术论文写作(第二版)
- 实习证明模板(两种格式)
评论
0/150
提交评论