Silverlight探索记录--用WCF和Silverlight建立"准"聊天室(1)

使用Silverlight构建互联网应用是再方便不过的了.WCF服务更是将以前与服务器的复杂沟通简化到像调用类库一样简单.

项目一放下就没什么事做.写个小东西拿出来分享~

我毕竟年龄小,写的东西没有什么技术含量,大家见谅了.先谢谢了.

 -------------------------------------还是我,那个没资历,没资格学习Silverlight的人  => 回应昨天文章里进行人身攻击的那些过来人  ----------------------------------------------------------

 

 

演示:

 

初衷:

实在不想再像以前一样使用SOCKET编程了,只是觉得用过了,但没有用到Silverlight最顺手的地方.Silverlight为了安全,把Socket能访问的端口限制在了几个上,总觉得很不爽.想想Silverlight总是可以和Server用WCF无缝集成的吧,就用WCF服务和Silverlight写个"准"聊天室.

WCF不具备操作客户端的能力,所以就没有C/S结构的设计了,变成C请求S,S回应的形式

 

思路:

类似留言板但不需要远程存储的一个程序。否则容易出现并发访问的问题。--也更不需要WCF了,直接POST好了。。

 

首先做了简单的WCF端设计

主要具备以下几个函数

 

Login

GetUserList

Logoff

SendMessage

GetMessage

 

还有两个DataContract类

Message --存储一条消息的相关信息

UserIdentify --提供用户验证策略 --这里面没有实现,但是由于留出了这个类,后面的再开发会很容易。

 

 

在SL项目里面 添加一个WCF服务,然后加入下面的代码

[ServiceContract(Namespace = "")]
            [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
            public class ChatServer
            {
            static Dictionary<string, List<Message>> MessageList = new Dictionary<string, List<Message>>();
            static Dictionary<string,UserIdentify> UserList = new Dictionary<string,UserIdentify>();
            [OperationContract]
            public UserIdentify Login(string UserName)
            {
            UserIdentify UI = new UserIdentify();
            UI.UserName = UserName;
            if (UserList.Keys.Contains(UserName))
            {
            return null;
            }
            else
            {
            UserList.Add(UserName,UI);
            return UI;
            }
            }
            [OperationContract]
            public List<string> GetUserList(UserIdentify YourIdentify)
            {
            if (UserList.Keys.Contains(YourIdentify.User_Name))
            {
            return new List<string>(UserList.Keys);
            }
            else
            {
            return null;
            }
            }
            [OperationContract]
            public UserIdentify Logoff(UserIdentify UI)
            {
            //Clean Data_List
            if (UserList.Keys.Contains(UI.User_Name))
            {
            UserList.Remove(UI.User_Name);
            MessageList.Remove(UI.User_Name);
            return null;
            }
            else
            {
            return null;
            }
            }
            [OperationContract]
            public Message SendMessage(UserIdentify Sender,string TargetName,string MessageBody)
            {
            if (UserList.Keys.Contains(TargetName))
            {
            Message M = new Message();
            M.SentTime = DateTime.Now.ToShortTimeString();
            M.To = UserList[TargetName];
            M.From = Sender;
            M.MessageBody = MessageBody;
            if (!(MessageList.Keys.Contains(M.To.User_Name)))
            {
            MessageList.Add(M.To.User_Name, new List<Message>());
            }
            MessageList[M.To.User_Name].Add(M);
            return M;
            }
            else
            {
            return null;
            }
            }
            [OperationContract]
            public List<Message> GetMessage(UserIdentify Receiver)
            {
            if (MessageList.Keys.Contains(Receiver.User_Name))
            {
            //T-COPY, Clear=>
            List<Message> Temp = new List<Message>(MessageList[Receiver.User_Name]);
            //Clear.
            MessageList.Remove(Receiver.User_Name);
            //Post-//
            return Temp;
            }
            else
            {
            return null;
            }
            }
            }
            [DataContract, ServiceKnownType(typeof(UserIdentify))]
            public class UserIdentify
            {
            //构建该类,可以为后期添加密码和GUID做准备
            [DataMember]
            internal string UserName;
            [DataMember]
            public string User_Name { get { return UserName; } set { } }
            }
            [DataContract, ServiceKnownType(typeof(Message))]
            public class Message
            {
            [DataMember]
            public UserIdentify From { get; set; }
            [DataMember]
            public string MessageBody { get; set; }
            [DataMember]
            public string Senttime { get { return SentTime; } set { } }
            [DataMember]
            internal UserIdentify To;
            [DataMember]
            internal string SentTime;
            }
            
代码很简单,也很少。不多作解释了。(如果需要解释就在评论里吼一句,下一篇加注释)
 
服务端搞定之后,在SL客户端添加服务引用。
image
 
然后写一下Page.XAML的界面——当然,很简单了。我不太喜欢Blend,但是手写效果确实不太好。。
 
<Grid x:Name="LayoutRoot" Background="White">
            <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="30"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="30"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <TextBox x:Name="Text_UserName" Grid.Row="0" Text="User"/>
            <TextBox x:Name="Text_MsgBody" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="3" Foreground="White" Background="Black" Text="MsgBody"/>
            <Button x:Name="Btn_Login" Grid.Row="1" Content="Login" Click="B_Click"/>
            <Button x:Name="Btn_Send" Grid.Row="3" Content="Send" Click="SendMsg"/>
            <ListBox x:Name="List_UserList" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="1" Background="SlateBlue"/>
            <TextBox x:Name="Text_Messages" IsReadOnly="True" Grid.Column="1" Grid.ColumnSpan="3" Grid.RowSpan="3" Background="Black" Foreground="White"/>
            </Grid>

效果很难看,但是够用了---具体的把它变成能用的东西要到下次了

image

 

下一步是Page.Xaml.cs的代码。看起来有点冗杂,但其实都是Async惹的祸。

直接贴过来了

using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Net;
            using System.Windows;
            using System.Windows.Controls;
            using System.Windows.Documents;
            using System.Windows.Input;
            using System.Windows.Media;
            using System.Windows.Media.Animation;
            using System.Windows.Shapes;
            using WCFChat.ChatServerRef;
            using System.ServiceModel;
            namespace WCFChat
            {
            public partial class Page : UserControl
            {
            public Page()
            {
            InitializeComponent();
            CSC = new WCFChat.ChatServerRef.ChatServerClient(new BasicHttpBinding(),new EndpointAddress("http://192.168.1.102/Chat/ChatServer.svc"));
            CSC.LoginCompleted += new EventHandler<WCFChat.ChatServerRef.LoginCompletedEventArgs>(CSC_LoginCompleted);
            CSC.LogoffCompleted += new EventHandler<LogoffCompletedEventArgs>(CSC_LogoffCompleted);
            CSC.GetUserListCompleted += new EventHandler<GetUserListCompletedEventArgs>(CSC_GetUserListCompleted);
            CSC.SendMessageCompleted += new EventHandler<SendMessageCompletedEventArgs>(CSC_SendMessageCompleted);
            CSC.GetMessageCompleted += new EventHandler<GetMessageCompletedEventArgs>(CSC_GetMessageCompleted);
            Refresher = new Storyboard();
            Refresher.Stop();
            Refresher.Completed += new EventHandler(LetusRefresh);
            Refresher.Duration = new Duration(new TimeSpan(0, 0, 0, 1));
            Refresher.AutoReverse = true;
            }
            public ChatServerRef.ChatServerClient CSC;
            Storyboard Refresher;
            bool IsOnLine;
            public UserIdentify UI;
            private void B_Click(object sender, RoutedEventArgs e)
            {
            if (!IsOnLine)
            {
            CSC.LoginAsync(Text_UserName.Text);
            }
            else
            {
            CSC.LogoffAsync(UI);
            }
            }
            void CSC_LoginCompleted(object sender, WCFChat.ChatServerRef.LoginCompletedEventArgs e)
            {
            if (e.Result == null)
            {
            Btn_Login.Content = "Failed";
            IsOnLine = false;
            Text_UserName.IsReadOnly = false;
            UI = null;
            }
            else
            {
            Text_UserName.IsReadOnly = true;
            Btn_Login.Content = "LogOff";
            IsOnLine = true;
            UI = e.Result;
            RefreshNameList();
            Refresher.Begin();
            }
            }
            void CSC_LogoffCompleted(object sender, LogoffCompletedEventArgs e)
            {
            if (e.Result == null)
            {
            //Finished
            Btn_Login.Content = "Login";
            IsOnLine = false;
            Text_UserName.IsReadOnly = false;
            UI = null;
            Refresher.Stop();
            }
            else
            {
            Btn_Login.Content = "Failed";
            }
            }
            void CSC_GetUserListCompleted(object sender, GetUserListCompletedEventArgs e)
            {
            if (e.Result != null)
            {
            List_UserList.Items.Clear();
            foreach (string Name in e.Result)
            {
            ListBoxItem LBI = new ListBoxItem();
            LBI.Content = Name;
            List_UserList.Items.Add(LBI);
            }
            }
            }
            void CSC_SendMessageCompleted(object sender, SendMessageCompletedEventArgs e)
            {
            if (e.Result == null)
            {
            OutputLine("Error Sending Msg!");
            }
            else
            {
            OutputLine(e.Result.SentTime);
            OutputLine(string.Format("I said to {0}: {1}", e.Result.To.UserName, e.Result.MessageBody));
            }
            }
            void CSC_GetMessageCompleted(object sender, GetMessageCompletedEventArgs e)
            {
            if (e.Result != null)
            {
            if (e.Result.Count != 0)
            {
            foreach (Message M in e.Result)
            {
            OutputLine(M.SentTime);
            OutputLine(string.Format("{0} said to me: {1}", M.From.UserName, M.MessageBody));
            }
            }
            }
            }
            private void RefreshNameList()
            {
            CSC.GetUserListAsync(UI);
            }
            private void SendMsg(object sender, RoutedEventArgs e)
            {
            if (Btn_Send.Tag != null)
            {
            CSC.SendMessageAsync(UI, Btn_Send.Tag.ToString(), Text_MsgBody.Text);
            }
            }
            private void OutputLine(string Line)
            {
            Text_Messages.Text += "\n" + Line;
            Text_Messages.Select(Text_Messages.Text.Length - 1, 0);
            }
            private void LetusRefresh(object sender, EventArgs e)
            {
            CSC.GetMessageAsync(UI);
            CSC.GetUserListAsync(UI);
            Refresher.Begin();
            }
            private void List_UserList_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
            if (e.AddedItems.Count > 0)
            {
            Btn_Send.Tag = (e.AddedItems[e.AddedItems.Count - 1] as ListBoxItem).Content.ToString(); ;
            Btn_Send.Content = "To " + Btn_Send.Tag.ToString();
            }
            }
            }
            }

 

 

对于这里的广大高手来说,这点东西很易懂...:)所以偷懒不搞注释了 :)    (小朋友能力有限..还要上新东方..英文课...注释没时间了)

 

好啦,一切搞定,当然要注意了,我上面的服务绑定是绑定在我家的局域网IP上,大家要自己修改哦

比如修改成你们的部署目录:

http://localhost/...svc  就好了

这个东西Bug很多,比如说下线的时候需要手动下线。当然,我回去完善下,把心跳信息加上就好了~

上面的Refresher就是个Timer,可以通过就该她的更新时间来减少网络开销。

 

就这样了。我是小朋友,我是新手。没有技术含量 只是分享罢了~代码写得不好见谅   :)

 --------------------------------------------------------------------------------------------------------------------------------

最后的最后:我觉得 CNBLOG是个好地方,我觉得我应该有资格来共享我的小经验吧 :)

 

 

忘记了:项目打包下载:

https://files.cnblogs.com/struggle-luan/WCFChat.rar

 

关于演示的问题,实在没有办法。。我没有服务器,其他虚拟主机不支持WCF,不知大虾们都是怎么做的。。

posted @ 2009-01-02 18:03  struggle-luan  阅读(3720)  评论(9编辑  收藏  举报