服务器端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节并通知其它用户.
项目解决方案和类图
附录:
推荐使用Spark作为客户端