您的位置:首页 > 其它

使用SilverLight构建插件式应用程序(九) —聊天插件客户端的实现

2008-10-30 00:41 621 查看
这次是一个在线聊天的插件,插件参考了MSDN中Duplex WCF服务的实现和网上一些聊天程序,基本可以实现用户登录和聊天,如果用户不存在就保存聊天数据到数据库,等用户下次登陆的时候读入。

这个是聊天时候的图例:


为什么不使用在客户单添加WCF服务的方法生成代理类然后进行编程序,首先是生成的代理类我没有发现可以实现WCF双向传输的方法,在网上也没有找到,所以就参照msdn和一些网上的代码,自己编写代理类进行操作:

1: 定义聊天时候传输聊天的数据实体,参照服务器端的聊天数据实体的定义,在客户端定义如下的实体:

[DebuggerStepThroughAttribute()]

[GeneratedCodeAttribute("System.Runtime.Serialization", "3.0.0.0")]

[DataContractAttribute(Name = "ChatMessage", Namespace = "http://schemas.datacontract.org/2004/07/")]

public partial class ChatMessage : object

{

private string DataField;

private string FromField;

private string ToField;

private int TypeField;

private byte[] ContentField;

[DataMemberAttribute()]

public string Data

{

get

{

return this.DataField;

}

set

{

if ((object.ReferenceEquals(this.DataField, value) != true))

{

this.DataField = value;

}

}

}

[DataMemberAttribute()]

public string From

{

get

{

return this.FromField;

}

set

{

if ((object.ReferenceEquals(this.FromField, value) != true))

{

this.FromField = value;

}

}

}

[DataMemberAttribute()]

public string To

{

get

{

return this.ToField;

}

set

{

if ((object.ReferenceEquals(this.ToField, value) != true))

{

this.ToField = value;

}

}

}

[DataMemberAttribute()]

public int Type

{

get

{

return this.TypeField;

}

set

{

if ((object.ReferenceEquals(this.TypeField, value) != true))

{

this.TypeField = value;

}

}

}

[DataMemberAttribute()]

public byte[] Content

{

get

{

return this.ContentField;

}

set

{

if ((object.ReferenceEquals(this.ContentField, value) != true))

{

this.ContentField = value;

}

}

}

这个实体类的定义其实是可以自动生成的。

2:把对WCF服务的访问方法单独处理成一个对象,模式和结构基本和MSDN上那篇文章一样:

public void Start()

{

// Instantiate the binding and set the time-outs

PollingDuplexHttpBinding binding = new PollingDuplexHttpBinding()

{

ReceiveTimeout = TimeSpan.FromSeconds(45),

InactivityTimeout = TimeSpan.FromMinutes(1)

};

// Instantiate and open channel factory from binding

IChannelFactory<IDuplexSessionChannel> factory =

binding.BuildChannelFactory<IDuplexSessionChannel>(new BindingParameterCollection());

IAsyncResult factoryOpenResult =

factory.BeginOpen(new AsyncCallback(OnOpenCompleteFactory), factory);

if (factoryOpenResult.CompletedSynchronously)

{

CompleteOpenFactory(factoryOpenResult);

}

}

void OnOpenCompleteFactory(IAsyncResult result)

{

if (result.CompletedSynchronously)

return;

else

CompleteOpenFactory(result);

}

void CompleteOpenFactory(IAsyncResult result)

{

IChannelFactory<IDuplexSessionChannel> factory =

(IChannelFactory<IDuplexSessionChannel>)result.AsyncState;

factory.EndOpen(result);

// The factory is now open. Create and open a channel from the channel factory.

IDuplexSessionChannel channel =

factory.CreateChannel(new EndpointAddress(ServiceUrl));

IAsyncResult channelOpenResult =

channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);

if (channelOpenResult.CompletedSynchronously)

{

CompleteOpenChannel(channelOpenResult);

}

}

void OnOpenCompleteChannel(IAsyncResult result)

{

if (result.CompletedSynchronously)

return;

else

CompleteOpenChannel(result);

}

上边这段代码基本是固定模式,表示使用channel模式进行访问WCF服务,打开完成服务之后,开始进行第一次通信,就是发送信条信息,然后进入消息等待模式:

void CompleteOpenChannel(IAsyncResult result)

{

channel = (IDuplexSessionChannel)result.AsyncState;

channel.EndOpen(result);

ChatMessage chatMessage = new ChatMessage();

chatMessage.From = UserName;

chatMessage.To = "SystemServer";

chatMessage.Data = System.DateTime.Now.ToString();

// Channel is now open. Send message

Message message =

Message.CreateMessage(channel.GetProperty<MessageVersion>(),

"WindCloud/WSChatService/Heart", chatMessage);

IAsyncResult resultChannel =

channel.BeginSend(message, new AsyncCallback(OnSend), channel);

if (resultChannel.CompletedSynchronously)

{

CompleteOnSend(resultChannel);

}

//Start listening for callbacks from the service

ReceiveLoop(channel);

}

至此,客户端就会一直等待服务器发来消息,一旦接收到消息,就进入下边的消息处理过程:

void ReceiveLoop(IDuplexSessionChannel channel)

{

// Start listening for callbacks.

IAsyncResult result = channel.BeginReceive(new AsyncCallback(OnReceiveComplete), channel);

if (result.CompletedSynchronously) CompleteReceive(result);

}

void OnReceiveComplete(IAsyncResult result)

{

if (result.CompletedSynchronously)

return;

else

CompleteReceive(result);

}

void CompleteReceive(IAsyncResult result)

{

//A callback was received so process data

IDuplexSessionChannel channel = (IDuplexSessionChannel)result.AsyncState;

try

{

Message receivedMessage = channel.EndReceive(result);

// Show the service response in the UI.

if (receivedMessage != null)

{

ChatMessage text = receivedMessage.GetBody<ChatMessage>();

_UiThread.Post(Client.ProcessData, text);

}

ReceiveLoop(channel);

}

catch (CommunicationObjectFaultedException exp)

{

_UiThread.Post(delegate(object msg) { System.Windows.Browser.HtmlPage.Window.Alert(msg.ToString()); }, exp.Message);

}

}

上边的代码由于都是使用的异步模式,看起来还是比较多的,但是道理还不是很复杂。这些都是模式相对固定的代码,下边就是我们针对聊天需要的代码了:

告诉系统,加入到聊天室:

public void Join(string userName)

{

ChatMessage chatMessage = new ChatMessage();

chatMessage.From = userName;

chatMessage.To = "";

chatMessage.Data = "";

//

Message message = Message.CreateMessage(channel.GetProperty<MessageVersion>(), "WindCloud/WSChatService/Join", chatMessage);

IAsyncResult resultChannel = channel.BeginSend(message, new AsyncCallback(OnSend), channel);

if (resultChannel.CompletedSynchronously)

{

CompleteOnSend(resultChannel);

}

}

最主要的一个方法,说话:

public void Say(ChatMessage chatMessage)

{

// The channel is now open. Send a message.

Message message = Message.CreateMessage(channel.GetProperty<MessageVersion>(), "WindCloud/WSChatService/Say", chatMessage);

try

{

IAsyncResult resultChannel = channel.BeginSend(message, new AsyncCallback(OnSend), channel);

if (resultChannel.CompletedSynchronously)

{

CompleteOnSend(resultChannel);

}

}

catch (Exception ex)

{

//channel = factory.CreateChannel(new EndpointAddress("http://localhost/ChatServer/Service.svc"));

IAsyncResult channelOpenResult = channel.BeginOpen(new AsyncCallback(OnOpenCompleteChannel), channel);

if (channelOpenResult.CompletedSynchronously)

{

CompleteOpenChannel(channelOpenResult);

}

IAsyncResult resultChannel = channel.BeginSend(message, new AsyncCallback(OnSend), channel);

if (resultChannel.CompletedSynchronously)

{

CompleteOnSend(resultChannel);

}

}

}

客户单服务重要的代码差不多就是这些,需要明白如何发送消息,如果进入消息接受模式。下面看看界面如何处理:

private void StartServer()

{

Uri uri = System.Windows.Browser.HtmlPage.Document.DocumentUri;

string host = uri.AbsoluteUri;

host = host.Substring(0, host.Length - uri.LocalPath.Length);

string servicePath = "/Services/WSChat.svc";

string serviceUri = host + servicePath;

chatSerrice = new ChatServer(this, serviceUri, App.GetLoginUser().UserName);

chatSerrice.Start();

chatSerrice.Join(App.GetLoginUser().UserName);

//

_Timer = new Timer(new TimerCallback(_Timer_Elapsed), null, 30000, Timeout.Infinite);

}

建立新的服务,然后定时发送心跳信息:

private void CreateChat(string friendName)

{

///

if (!chatUsers.ContainsKey(friendName))

{

UserChat userChat = new UserChat();

userChat.txtTitle.Text = "与 " + friendName + " 交谈中";

userChat.OnSend += (o, ev) =>

{

ev.From = App.GetLoginUser().UserName;

chatSerrice.Say(ev);

};

userChat.UserName = friendName;

userChat.OnCloseed += (o, ev) =>

{

chatUsers.Remove(ev);

for (int i = 0; i < canvasChat.Children.Count; i++)

{

if ((canvasChat.Children[i] as UserChat).UserName == ev)

{

canvasChat.Children.RemoveAt(i);

}

}

};

chatUsers.Add(friendName, userChat);

this.canvasChat.Children.Add(userChat);

}

}

针对每个不同的聊天对象,建立独立的窗口。

public void ProcessData(Object receivedData)

{

if (receivedData != null)

{

ChatMessage cm = receivedData as ChatMessage;

if (cm.Type > 0)

{

//聊天窗口是否包含

if (!chatUsers.ContainsKey(cm.From))

{

CreateChat(cm.From);

}

if (chatUsers.ContainsKey(cm.From))

{

string txt = "";

txt = cm.From + " 说:" + cm.Data + ""n"; ;

chatUsers[cm.From].txtChat.Text += txt;

}

}

}

}

接受到不同的信息之后,把对应的消息显示到不同的窗口上,免得混杂在一起:

至此,聊天的主要功能完成。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: