Shenjk

天地生人,有一人应有一人之业;人生在世,生一日当尽一日之勤

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

这是我在网上找的示例,只是转载供朋友参考学习

本实例是基agsXMPP上开发的,agsXMPP是C#写的支持开源XMPP协议软件,我们可以在agsXMPP上快速构建自已的即时通讯平台,我的这个例子只是修改了服务器端,因为agsXMPP本身自带的服务器端没有实现聊天功能、签名和登录密码认证。

服务器端XmppSeverConnection类事件

//在流开始时触发,一般是最初的响应流
streamParser.OnStreamStart += new StreamHandler(streamParser_OnStreamStart);

//在流结束时触发,一般是发送</stream:stream>并关闭套接字连接streamParser.OnStreamEnd += new StreamHandler(streamParser_OnStreamEnd);

//在接收到流结点时触发,这是用得最多的,常用的<message>消息,<Presence>出席消息,< IQ>请求应答消息都在这里处理
streamParser.OnStreamElement += new StreamHandler(streamParser_OnStreamElement);

//此处处理大部份的消息,包括消息路由
private void streamParser_OnStreamElement(object sender, Node e)
         {
             Console.WriteLine(
"OnStreamElement: " + e.ToString());
            
if (e.GetType() == typeof(Presence))
             {
             
// 路由presences节
             }
            
else if (e.GetType() == typeof(Message))
             {
                
// 路由messages节
             }
            
else if (e.GetType() == typeof(IQ))
             {
                
//处理IQ节
             }
         }

/// <summary>
        
/// IQ节处理函数
        
/// </summary>
        
/// <param name="iq">.</param>
        private void ProcessIQ(IQ iq)
         {
            
if (iq.Query.GetType() == typeof(Auth))
             {
                Auth auth
= iq.Query as Auth;
                
this.Username = auth.Username.ToString();

                
switch (iq.Type)
                 {
                    
case IqType.get:
                         iq.SwitchDirection();
                         iq.Type
= IqType.result;
                         auth.AddChild(
new Element("password"));
                         auth.AddChild(
new Element("digest"));
                         Send(iq);
                        
break;

                    
case IqType.set:
                        
// 进行登录认证
                        if (AccountBus.CheckLogin(auth.Username, auth.Digest, this.SessionId))
                         {
                             iq.SwitchDirection();
                             iq.Type
= IqType.result;
                             iq.Query
= null;
                             Send(iq);

                             Console.WriteLine(auth.Username
+ "登录了" + "    登录时间:" + System.DateTime.Now.ToString());

                         }
                       
else
                         {
//登录失败返回错误信息
                             iq.SwitchDirection();
                             iq.Type
= IqType.error;
                             iq.Query
= null;
                             Send(iq);
                         }
                        
break;
                 }
             }
            
else if (iq.Query.GetType() == typeof(Roster))
             {
                 ProcessRosterIQ(iq);
             }
         }

/// <summary>
        
/// 处理IQ节的杂项数据.
        
/// </summary>
        
/// <param name="iq">The iq.</param>
        private void ProcessRosterIQ(IQ iq)
         {
            
if (iq.Type == IqType.get)
             {
                
// 发送IQ节的杂项数据
              
//这里我用来下载好友列表
                 iq.SwitchDirection();
                 iq.Type
= IqType.result;
                 List
<string> friendList = new List<string>();
                 friendList
= AccountBus.GetFriendName(this.username);
                
foreach (string str in friendList)
                 {
                     RosterItem ri
= new RosterItem();
                     ri.Name
= str.Trim();
                     ri.Subscription
= SubscriptionType.both;
                     ri.Jid
= new agsXMPP.Jid(str.Trim() + "@localhost");
                     ri.AddGroup(
"localhost");
                     iq.Query.AddChild(ri);
                 }
                 Send(iq);
             }
         }


服务器端开启监听5222端口

 

while (running)
                 {
                    
////
                     allDone.Reset();
                    
// Start an asynchronous socket to listen for connections.
                     Console.WriteLine("等待连接");

                     listener.BeginAccept(
new AsyncCallback(AcceptCallback), null);

                    
//// 等待客户端连接                    
                      allDone.WaitOne();

                 }


如果收到客户端请求就异步调用AcceptCallback初始化套接字连接
,并为客户端建立一个通信线程,新建初始化套接字连接采用异步调
用读取套接字信息

public XmppSeverConnection(Socket sock)
             :
this()
         {
             m_Sock
= sock;
             m_Sock.BeginReceive(buffer,
0, BUFFERSIZE, 0, new AsyncCallback(ReadCallback), null);
             m_Sock.SendTimeout
= 100;
         }



客户端与服务器端的交互过程


  
1客户端异步向服务器端发送连接请求

<stream:stream to='localhost' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xml:lang='en'>

2服务器端收到请求,初始化回应流,并随机生成一相SessionID

<stream:stream xmlns:stream="http://etherx.jabber.org/streams" from="localhost" id="30e3b8c0" >

3等待服务器返回消息后客户端发送用户名(由于在客户端采用了异步调用
方式,所以UI界面感觉不到等待)

<iq xmlns="jabber:client" id="agsXMPP_1" type="get" to="localhost">
<query xmlns="jabber:iq:auth"><username>test</username></query></iq>

4服务器端收到用户名等待用户提供密码

<iq xmlns="jabber:client" from="localhost" type="result" id="agsXMPP_1">
<query xmlns="jabber:iq:auth"><username>test</username><password />
<digest /></query></iq>

5客户端提供加密后的密码

<iq xmlns="jabber:client" id="agsXMPP_2" to="localhost" type="set">
<query xmlns="jabber:iq:auth"><username>test</username>
<digest>e66557d2b67256bf7e9b317a51b6101674a56b5e</digest>
<resource>MiniClient</resource></query></iq>

6服务器端从数据库验证用户名和密码,并返回结果

iq xmlns="jabber:client" from="localhost" type="result" id="agsXMPP_2" />

7如果返回错误,客户端提示并终断连接,否则客户端发送响应数据

8 服务器端返回数据

9 客户端发送状态,

10服务器收到状态,发送IQ节并通知其它用户.

项目解决方案和类图


推荐使用Pandion作为客户端
服务器端下载      客户端下载
agsXMPP 源码下载
XMPP RFC 3920 可扩展消息出席协议
jabber官方网站

posted on 2009-06-23 18:20  shenjk  阅读(867)  评论(0编辑  收藏  举报