以下是最近搞的一个用asp.net实现的即时消息的小功能,由于消息的即时性,和网络的无状态无连接。因此曾一时间无法想到很好的解决方法,很多人都说可以使用socket编程来实现使用端口进行点对点即时通信。
可是,暂时没有看到现成实现的例子。于是,我还是使用了像实现简易聊天室那样的方式,使用了一个Application对象,全局维护一个用户消息表来实现从一个客户端到服务器,再从服务器到另一个客户端信息的传递(这也是问题的关键所在,我无法实现不通过服务器,来实现点对点消息的传递,就算是通过了服务器也无法不通过Application这种共享的方式来实现信息的传递)。
主要思路和实现方式如下:发送端:查看是否在线——>发送消息(写入数据库);接收端:
收到消息提示——>查看消息——>回复(写入数据库) or 忽略——>如果有下一条,则重复以上步骤。而最重要的部分:就是接收端如何接收消息,和无论接收端停留在哪个页面上,我们都要有办法来提示用户有新消息(事实上,这是一个OA系统的附带功能,也就是说用户当时可能正在干其他事情,但要有办法让他收到消息提示)。如何接收消息,其实就是一个非常简单的方式,那就是用一个Timer控件,间隔一段时间去Application对象中轮询侦听(这和你定时去刷新的作用差不多),是否有新消息待接收。而想在每个页面上都要实现有新消息就提示,则菜用了Frame的方式,思路很简单。你肯定要在一个全局性的框架页面上去提示用户,同时侦听消息也必须是在内嵌在这个Frame的页面上进行。
好了,下面就是相关示例代码:
首先登陆过后跳转到MainForm.aspx页面(由以下几个框架构成):
<frameset rows="129,*" cols="*" frameborder="NO" border="0" framespacing="0">
<frame src="Top.aspx" mce_src="Top.aspx" name="topFrame" scrolling="NO" noresize>
<frameset cols="250,*" frameborder="NO">
<frame src="Left.aspx" mce_src="Left.aspx" name="leftFrame" scrolling="yes" noresize>
<frameset rows="30,*" frameborder="NO" id="right">
<frame scrolling="no" name="right_topFrame" src="ShowMsg.aspx" mce_src="ShowMsg.aspx" frameborder="0">
<frame src="Choose.aspx" mce_src="Choose.aspx" name="rightFrame" noresize scrolling="yes">
</frameset>
</frameset>
</frameset>
<body>
<noframes>
</noframes>
</body>
其中,name为right_topFrame的Frame则是用来侦听消息的框架页,其他所有页面都是在name为rightFrame的Frame中实现,在此不再多说。
我们在Application对象中维护下面一个用户对象的数组,其中Users.cs如下:
public class Users
{
private string userID;
private string userName;
private bool isOnline;
private StringBuilder message_text;
private DateTime message_time;
private string message_to;
private string message_from;
private Queue message_queue;
public Users(string uid)
{
this.userID = uid;
this.isOnline = false;
message_text = new StringBuilder("");
message_to = userID;
message_from = userID;
//设置一个以前的时间
message_time = Convert.ToDateTime("2000-01-01 00:00:00");
message_queue = new Queue();
Message = new messageInfo(Message_text.ToString(), Message_time,Message_from);
}
//帐号
public string UserID
{
get { return userID; }
set { userID = value; }
}
//姓名
public string UserName
{
get { return userName; }
set { userName = value; }
}
//存储消息
public StringBuilder Message_text
{
get { return message_text; }
set { message_text = value; }
}
//是否在线
public bool IsOnline
{
get { return isOnline; }
set { isOnline = value; }
}
//信息接收者
public string Message_to
{
get { return message_to; }
set { message_to = value; }
}
//信息发送者
public string Message_from
{
get { return message_from; }
set { message_from = value; }
}
//接受信息的时间
public DateTime Message_time
{
get { return message_time; }
set { message_time = value; }
}
//用于接收消息的消息队列
public Queue Message_queue
{
get { return message_queue; }
set { message_queue = value; }
}
//用来存储消息相关信息
public struct messageInfo
{
public string message_text;
public DateTime message_time;
public string message_from;
public messageInfo(string m_text,DateTime m_time, string m_from)
{
message_text = m_text;
message_time = m_time;
message_from = m_from;
}
}
/// <summary>
/// 定义一个消息结构体类型的对象
/// </summary>
public messageInfo Message;
}
为便于访问,我们先从数据库中取得相关用户的id,姓名等信息,在Users数组中对各个User对象进行初始化,为便于以键值对的形式访问,我们先将这个Users数组中的每个元素都存入放在一个以其ID作为key的HashTable中;这些操作都必须在应用程序刚启动时就必须完成,在整个应用程序运行期间,都必须去维护这个用户消息表,所以 Global.asax中Application_Start方法实现如下:
protected void Application_Start(object sender, EventArgs e)
{
Users_DS ds = new Users_DS();
Users_DSTableAdapters.UsersTableAdapter userAda = new IM.Users_DSTableAdapters.UsersTableAdapter();
userAda.FillAll(ds.Users);
int count=ds.Users.Rows.Count;
Common.Users[] users = new Common.Users[count];
Hashtable ht_users = new Hashtable();
for (int i = 0; i < count; i++)
{
users[i] = new Common.Users(ds.Users.Rows[i][0].ToString());
users[i].UserName = ds.Users.Rows[i][1].ToString();
ht_users[users[i].UserID] = users[i];
}
if (Application["Users"] == null)
{
Application["Users"] = ht_users;
}
}
大致原理就是如此。
原文发布时间为:2010-04-05
本文作者:vinoYang