服務器端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節
}
}
/// 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);
}
}
/// 處理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端口
{
////
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("等待連接");
listener.BeginAccept(new AsyncCallback(AcceptCallback), null);
//// 等待客戶端連接
allDone.WaitOne();
}
如果收到客戶端請求就異步調用AcceptCallback初始化套接字連接
,併爲客戶端建立一個通信線程,新建初始化套接字連接採用異步調
用讀取套接字信息
: 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作爲客戶端