zoukankan      html  css  js  c++  java
  • 使用SilverLight构建插件式应用程序(九) —聊天插件客户端的实现

    这次是一个在线聊天的插件,插件参考了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;
                        }
                    }
                }
            }

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

     至此,聊天的主要功能完成。

  • 相关阅读:
    GZOI 2017配对统计 树状数组
    关于线段树的一些问题
    BZOJ 压力 tarjan 点双联通分量+树上差分+圆方树
    洛谷4552 差分
    洛谷5026 Lycanthropy 差分套差分
    【锁】MySQL和Oracle行锁比较
    oracle体系结构
    【加密】RSA验签及加密
    【Shiro】SpringBoot集成Shiro
    【Eureka】实现原理
  • 原文地址:https://www.cnblogs.com/songsgroup/p/1322611.html
Copyright © 2011-2022 走看看