首页 > 代码库 > 谁说长连接、长轮询必须用异步!

谁说长连接、长轮询必须用异步!

看了好多帖子一说长连接、长轮询、Comet之类的都是用的服务端的异步页面,大致如下:

protected void Page_Load(object sender, EventArgs e)
{
    AddOnPreRenderCompleteAsync(new BeginEventHandler(BeginAsyncOperation),new EndEventHandler(EndAsyncOperation));
}

 

而且每个人的异步页面的用法还都不一样。搞的我这个以前没用过异步页面的程序猿一头雾水,于是就想能不能不用异步页面实现长连接。经过一番研究还是可以的,不敢独享,拿出来大家给看看,有不到之处还望指点。

这个例子是一个群聊的demo,总共就两个页面一个登陆页,一个是聊天页,先看登陆页面:

我叉,这也太简单点。恩,没办法,本人业余时间有限,主要说长连接的事。点击登陆后台代码:

1 protected void btnLogin_Click(object sender, EventArgs e)
2     {
3         string name=txtName.Text.Trim();
4         if (string.IsNullOrEmpty(name) == false)
5         {
6             Session["name"] = name;
7             Response.Redirect("Chat.aspx");
8         }
9     }

再看重点的Chat.aspx页面:

前台:

 1 <%@ Page Language="C#" AutoEventWireup="true" EnableSessionState="ReadOnly" CodeFile="Chat.aspx.cs" Inherits="Chat" %>
 2 
 3 <!DOCTYPE html>
 4 
 5 <html xmlns="http://www.w3.org/1999/xhtml">
 6 <head>
 7 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
 8     <title></title>
 9     <script src="jquery-1.7.1.min.js"></script>
10     <script type="text/javascript">
11         //长轮询获取信息
12         function LongPolling() {
13             $.ajax(
14             {
15                 url: "Chat.aspx",
16                 type: "post",
17                 data: { "action": "get" },
18                 success: function (data){
19                     $("#content").append(data + "<br>");
20                     LongPolling();
21                 },
22                 error: function (xhr, info, obj) {
23                     $("#content").append(info + "<br>");
24                     LongPolling();
25                 }
26             });
27         }
28         LongPolling();
29 
30         //发送消息
31         function Send() {
32             if (!$("#msg").val())
33             {
34                 alert("不能发空白哦!");
35                 return;
36             }
37             $.ajax(
38             {
39                 url: "Chat.aspx",
40                 type: "post",
41                 data: { "action": "send", "msg": $("#msg").val() },
42                 success: function (data) 
43                 {
44                     //貌似什么也不用做
45                 }
46             });
47         }
48     </script>
49 </head>
50 <body>
51      <div id="content" style="width:500px; height:300px; overflow-y:auto; border:1px solid red;"></div>
52      <input type="text" id="msg" />
53      <input type="button" value="发送" onclick="Send()"/>
54 </body>
55 </html>

后台代码:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Threading;
  5 
  6 /// <summary>
  7 /// 注意:对应aspx页面必须加上EnableSessionState="ReadOnly"否则会造成请求阻塞
  8 /// </summary>
  9 public partial class Chat : BasePage
 10 {
 11     //全局静态变量:待发送的聊天信息
 12     public static List<ChatObj> ChatObjList = new List<ChatObj>();
 13     //全局静态变量:用户列表
 14     public static List<string> UserList = new List<string>();
 15 
 16     protected void Page_Load(object sender, EventArgs e)
 17     {
 18         //json返回的消息
 19         string msg = string.Empty;
 20         //验证是否登录了
 21         if (Session["name"] == null)
 22         {
 23             Response.Redirect("Login.aspx"); 
 25         }
 26         //操作 
 27         string action = Request["action"];
 28         //发送信息
 29         if (action == "send")
 30         {
 31             if (string.IsNullOrEmpty(Request["msg"]) == false)
 32             {
 33                 //模拟群聊 当前用户发的消息分发给在线的每一个人
 34                 foreach (var user in UserList)
 35                 {
 36                     ChatObjList.Add(new ChatObj()
 37                     {
 38                         From = Session["name"].ToString(),
 39                         To = user,
 40                         Message = Request["msg"],
 41                         SendTime = DateTime.Now
 42                     });
 43                 }
 44             }
 45             else
 46             {
 47                 msg = "发送信息不能为空";
 48             }
 49         }
 50         else if (action == "get")//获取信息
 51         {
 52             //这里是关键 检查是否有自己的消息,如果有则返回,没有则一直循环休眠
 53             while (true)
 54             {
 55                 ChatObj[] chatObjList = ChatObjList.AsEnumerable().Where(o => o.To == Session["name"].ToString()).ToArray();
 56                 if (chatObjList == null || chatObjList.Length == 0)
 57                 {
 58                     Thread.Sleep(1000);//休眠一秒
 59                 }
 60                 else
 61                 {
 62                     msg = string.Empty;
 63                     foreach (var item in chatObjList)
 64                     {
 65                         msg += item.From + "说:" + item.Message+"<br>";
 66                         ChatObjList.Remove(item);
 67                     }
 68                     break;
 69                 }
 70             }
 71         }
 72         Response.Write(msg);
 73         
 74         if (string.IsNullOrEmpty(action) == false)
 75         {
 76             //如果是ajax请求则终止响应,否则会输出整个页面的html
 77             Response.End();
 78         }
 79         else
 80         {
 81             //首次加载则把用户加入到用户列表 不太完善主要演示longpolling
 82             if (UserList.Contains(Session["name"].ToString())==false)
 83             {
 84                 UserList.Add(Session["name"].ToString());
 85             }
 86         }
 87     }
 88 }
 89 
 90 /// <summary>
 91 /// 发送消息对象
 92 /// </summary>
 93 public class ChatObj
 94 {
 95     /// <summary>
 96     /// 发送方
 97     /// </summary>
 98     public string From { get; set; }
 99     /// <summary>
100     /// 接收方
101     /// </summary>
102     public string To { get; set; }
103     /// <summary>
104     /// 消息
105     /// </summary>
106     public string Message { get; set; }
107     /// <summary>
108     /// 发送时间
109     /// </summary>
110     public DateTime SendTime { get; set; }
111 }

代码里注释已经很清楚了,确实也没用到异步页面。不过有一个要注意的地方就是Session的读写锁,以前没关注这个,必须用EnableSessionState="ReadOnly"把读锁之间、分开,否则会造成请求阻塞,用两个页面是为了把登陆时的写锁和获取信息时的读锁分开,在这个问题上花了不少功夫。Session读写锁请参考:http://www.cnblogs.com/OpenCoder/archive/2010/01/10/1643659.html

微软有个signalR的东西专门做消息推送、及时消息这块的,有兴趣的同学可以看看,已经第二版了。

下一个博客会写某驾校约车插件,用chrome插件开发。拜拜