惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Secure Thoughts
Security Latest
Security Latest
Simon Willison's Weblog
Simon Willison's Weblog
O
OpenAI News
GbyAI
GbyAI
L
LINUX DO - 最新话题
A
Arctic Wolf
T
Tor Project blog
G
GRAHAM CLULEY
I
InfoQ
博客园_首页
IT之家
IT之家
The Register - Security
The Register - Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
Blog — PlanetScale
Blog — PlanetScale
N
Netflix TechBlog - Medium
K
Kaspersky official blog
博客园 - 三生石上(FineUI控件)
S
SegmentFault 最新的问题
U
Unit 42
PCI Perspectives
PCI Perspectives
量子位
P
Palo Alto Networks Blog
S
Securelist
T
Troy Hunt's Blog
博客园 - 【当耐特】
Recorded Future
Recorded Future
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Security Affairs
Engineering at Meta
Engineering at Meta
T
The Blog of Author Tim Ferriss
博客园 - 聂微东
罗磊的独立博客
N
News and Events Feed by Topic
人人都是产品经理
人人都是产品经理
B
Blog RSS Feed
NISL@THU
NISL@THU
C
Cisco Blogs
T
Threatpost
有赞技术团队
有赞技术团队
Forbes - Security
Forbes - Security
Hugging Face - Blog
Hugging Face - Blog
Last Week in AI
Last Week in AI
T
The Exploit Database - CXSecurity.com
Cloudbric
Cloudbric
Cyberwarzone
Cyberwarzone
Google DeepMind News
Google DeepMind News
C
Cyber Attacks, Cyber Crime and Cyber Security

博客园 - 成都ABC

json 反序列化,高手请进 Django Message框架尝鲜 MVC 资料推荐 杂类 资料收集 收集到的非常好的第三方控件 Silverlight 开源框架 微软企业库资料 Silverlight 收藏网站 Silverlight的依赖属性与附加属性 对MVP和MVVM的一点看法 不需要Orchestration,通过Pipeline设定动态发送端口属性 使用BizTalk Server常见问题处理 在哪找biztalk的例子(转) 转:把自己搜集到的一些silverlight网站、第三方控件及开源代码与大家们分享(微软转载) 鸡肋: 评在IE下Siverlight3对快捷键的支持 siverlight高手解答:无法阻止silverlight把快捷键传递给IE7 微软:PHP在IIS 7上雄起 - 成都ABC - 博客园 WCF学习之:实例上下文模式和并发模式的性能影响
WCF综合运用之:聊天系统
成都ABC · 2009-09-30 · via 博客园 - 成都ABC

      使用WCF做聊天系统,也是对WCF的一个初步的综合运用,请看下面的整体框架
                          
      整个聊天系统需要考虑下面的几点:
      1、使用什么样的消息交换模式(如果不清楚参考:http://www.cnblogs.com/artech/archive/2007/03/02/661969.html):消息交换包括one-way,request/reply,duplex,前面两种相对的比较的简单,duplex关键是实现回调-服务端可以回调客户端的代码,所以我们需要定义两个接口:

    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IChatCallback))]
    public interface IChat  //服务端的接口
    {
        [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false, Action = "http://tempuri.org/IChat/Say")]
        void Say(string msg);   //公聊

        [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = false, Action = "http://tempuri.org/IChat/Whisper")]
        void Whisper(string to, string msg);    //私聊

        [OperationContract(IsOneWay = false, IsInitiating = true, IsTerminating = false, Action = "http://tempuri.org/IChat/Connect")]
        bool Connect(Person name);  //连接服务端 注册当前用户

        [OperationContract(IsOneWay = false, IsInitiating = false, IsTerminating = false, Action = "http://tempuri.org/IChat/PersonList")]
        List<Person> PersonList(Person name);   //获取所有的页面信息

        [OperationContract(IsOneWay = true, IsInitiating = false, IsTerminating = true, Action = "http://tempuri.org/IChat/Leave")]
        void Leave();   //注销用户
    }

    public interface IChatCallback //回调接口
    {
        [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/Receive")]
        void Receive(Person sender, string message);    //接收公聊信息

        [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/ReceiveWhisper")]
        void ReceiveWhisper(Person sender, string message); //接收私聊信息

        [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/UserEnter")]
        void UserEnter(Person person);  //接收进入聊天室的用户

        [OperationContract(IsOneWay = true, Action = "http://tempuri.org/IChat/UserLeave")]
        void UserLeave(Person person);  //移除在聊天室的用户
    }

     这里注意:要实现回调,必须在服务契约上指明回调的接口CallbackContract = typeof(IChatCallback),IChatCallback接口需要在客户端实现.
      2、使用什么样的实例模型,由于我们需要需要保持客户端的状态。我们需要使用PerSession模型(如果不清楚请参见:http://www.cnblogs.com/wanqiming/archive/2009/09/17/1568401.html)。代码如下:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]
    public class ChatService : IChat
    {
        #region Instance fields

        //定义一个静态对象用于线程部份代码块的锁定,用于lock操作
        private static Object syncObj = new Object();

        //创建一个IChatCallback 回调接口实例,接口成员始终是公共的,所有没有访问修饰符
        IChatCallback callback = null;

        //定义一个委托
        public delegate void ChatEventHandler(object sender, ChatEventArgs e);

        //定义一个静态的委托事件
        public static event ChatEventHandler ChatEvent;

        //创建委托(ChatEventHandler)的一个空实例
        private ChatEventHandler myEventHandler = null;

        //创建一个静态Dictionary(表示键和值)集合(字典),用于记录在线成员,Dictionary<(Of <(TKey, TValue>)>) 泛型类
        static Dictionary<Person, ChatEventHandler> chatters = new Dictionary<Person, ChatEventHandler>();

        //当前用户
        private Person person;

        //确认用户是否存在
        private bool checkIfPersonExists(string name)
        {
            foreach (Person p in chatters.Keys)
            {
                if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }
            return false;
        }

        //根据用户搜索字典中是否存在 ChatEventHandler , 以便调用执行
        private ChatEventHandler getPersonHandler(string name)
        {
            foreach (Person p in chatters.Keys)
            {
                if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    ChatEventHandler chatTo = null;
                    chatters.TryGetValue(p, out chatTo);
                    return chatTo;
                }
            }
            return null;
        }
       
        //根据用户名,寻找用户
        private Person getPerson(string name)
        {
            foreach (Person p in chatters.Keys)
            {
                if (p.Name.Equals(name, StringComparison.OrdinalIgnoreCase))
                {
                    return p;
                }
            }
            return null;
        }

        #endregion

        #region IChat implementation

        //连接服务器,如果存在重复的用户名,返回失败,否则连接成功,把当前用户加到服务器用户列表中
        public bool Connect(Person person)
        {
            bool userAdded = false;

            //创建一个新的委托代理,指向 MyEventHandler 方法
            myEventHandler = new ChatEventHandler(MyEventHandler);

            //锁定,保持lock块中的代码段始终只有一个线程在调用,原因是ConcurrencyMode.Multiple 为异步的多线程实例,存在并发竞争问题
            lock (syncObj)
            {
                if (!checkIfPersonExists(person.Name) && person != null)    //根据用户名判断用户名是否被占用
                {
                    this.person = person;
                    chatters.Add(person, MyEventHandler); //添加当前用户到服务器列表中
                    userAdded = true;
                }
            }
            if (userAdded)
            {
                callback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
                ChatEventArgs e = new ChatEventArgs();
                e.msgType = MessageType.UserEnter;
                e.person = this.person;
                BroadcastMessage(e);                
                ChatEvent += myEventHandler; //把当前实例加到事件中,便于调用对客户端的回调               
                return false;
            }
            else
            {
                return true;
            }
        }

        //获得聊天室中的所有用户信息
        public  List<Person> PersonList(Person person)
        {
            List<Person> list = new List<Person>();

            foreach (var p in chatters.Keys)
            {
                if( p.Name != person.Name )
                {
                    list.Add(p);
                }
            }
            return list;
        }

        //发送信息给所有的人
        public void Say(string msg)
        {
            ChatEventArgs e = new ChatEventArgs();
            e.msgType = MessageType.Receive;
            e.person = this.person;
            e.message = msg;
            BroadcastMessage(e);
        }

        //私聊 to 接受者姓名,msg 接受的信息
        public void Whisper(string to, string msg)
        {
            ChatEventArgs e = new ChatEventArgs();
            e.msgType = MessageType.ReceiveWhisper;
            e.person = this.person;
            e.message = msg;
            try
            {
                ChatEventHandler chatterTo;
                lock (syncObj)
                {
                    chatterTo = getPersonHandler(to);
                    if (chatterTo == null)
                    {
                        throw new KeyNotFoundException("The person whos name is " + to +
                                                        " could not be found");
                    }
                }                
                //异步回调
                chatterTo.BeginInvoke(this, e, new AsyncCallback(EndAsync), null);
            }
            catch (KeyNotFoundException)
            {
            }
        }

        //用户离开
        public void Leave()
        {
            if (this.person == null)
                return;

            ChatEventHandler chatterToRemove = getPersonHandler(this.person.Name);

            lock (syncObj)
            {
                chatters.Remove(this.person);
            }
            ChatEvent -= chatterToRemove;
            ChatEventArgs e = new ChatEventArgs();
            e.msgType = MessageType.UserLeave;
            e.person = this.person;
            this.person = null;
           
            //告示所有的人
            BroadcastMessage(e);
        }
        #endregion
        #region private methods

        //判断当前用户的操作
        private void MyEventHandler(object sender, ChatEventArgs e)
        {
            try
            {
                switch (e.msgType)
                {
                    case MessageType.Receive:
                        callback.Receive(e.person, e.message);
                        break;
                    case MessageType.ReceiveWhisper:
                        callback.ReceiveWhisper(e.person, e.message);
                        break;
                    case MessageType.UserEnter:
                        callback.UserEnter(e.person);
                        break;
                    case MessageType.UserLeave:
                        callback.UserLeave(e.person);
                        break;
                }
            }
            catch
            {
                Leave();
            }
        }

       
        //把信息发给所有的在线用户
        private void BroadcastMessage(ChatEventArgs e)
        {

            ChatEventHandler temp = ChatEvent;
          
            if (temp != null)
            {

                ChatEvent(this, e);
            }
        }


        //广播中线程调用完成的回调方法
        //功能:清除异常多路广播委托的调用列表中异常对象(空对象)
        private void EndAsync(IAsyncResult ar)
        {
            ChatEventHandler d = null;

            try
            {
                System.Runtime.Remoting.Messaging.AsyncResult asres = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
                d = ((ChatEventHandler)asres.AsyncDelegate);
                d.EndInvoke(ar);
            }
            catch
            {
                ChatEvent -= d;
            }
        }
        #endregion
    }

      需要注意的是:Persession下,需要回调客户端,可能会产生死锁,需要使用ConcurrencyMode = ConcurrencyMode.Multiple这种并发模式,但是这个不能保证数据的唯一,所以需要lock的方式。定义chatters来保持聊天室中所有用户的信息。运行的效果如下:
    

      看代码吧,下载地址:/Files/wanqiming/Chat.rar