• WCF也可以做聊天程序


    先看一个截图。

    上面的图,各位乍一看,可能会觉得是用Socket编写的聊天程序。告诉你吧,这玩意儿不是用Socket实现,呵呵,当然它的底层肯定与Socket有一定关系,我只说我的代码没有用到socket而已。

    那么,除了Socket可以用于通信,还有其他技术吗?有啊,首先,如果你足够强大,用HTTP也行,但HTTP初始化的过程貌似比较慢。那么还有吗?当然了,各位还记得.NET以前有一个很X但又很少被关注的技术——Remoting。用过吧?没用过也没关系,因为它已经有替代品了。

    这时候大家肯定想到WCF不是一盏“省油”的灯,其实不然,对比于用Socket要编写的代码数量和维护成本,用WCF编写网络通信程序,不仅省油,而且省时省力,最重要的是省心。所以,人的健康,心理健康是占主导的,你看一个心理不健康的人,其身体也不会健康到哪里去,今天这病明天那病。

    因而,编程这事啊,越省心越好,有利于我们的健康,赚钱永远不是目的,身心健康才是活在这个世界上的主旋律,至于你信不信,反正我深信不疑。

    我就这个WCF版的聊天程序的大致思路说一说。

    这个程序既可以充当服务器端也同时作为客户端,每个应用实例都扮演着双重角色。这里我不需要引用服务。首先看服务协定和服务类。

    using System;
    using System.ServiceModel;
    
    namespace ServiceDef
    {
        [ServiceContract]
        public interface IService
        {
            [OperationContract(IsOneWay = true)]
            void SendMessage(string msg);
        }
    
        /// <summary>
        /// 服务
        /// </summary>
        public class MyChatService : IService
        {
            /// <summary>
            /// 收到消息后引发的事件
            /// </summary>
            public event EventHandler<MessageReceiveEventArgs> MessageGot;
            public MyChatService()
            {
                MessageGot += new EventHandler<MessageReceiveEventArgs>(TestApp.Form1.GetMessageCallBack);
            }
            public void SendMessage(string msg)
            {
                if (MessageGot != null)
                {
                    MessageGot(this, new MessageReceiveEventArgs(msg));
                }
            }
        }
    
        /// <summary>
        /// 收到消息后引发事件的参数
        /// </summary>
        public class MessageReceiveEventArgs : EventArgs
        {
            private string m_Message = string.Empty;
            public MessageReceiveEventArgs(string message)
            {
                this.m_Message = message;
            }
    
            public string MessageText
            {
                get { return this.m_Message; }
            }
        }
    
    }


    服务协定没什么好看的了,相信大家都会写,这里的服务类与以往的有些不同,大家看到,里面定义了一个事件。那么,为什么要这样做呢?为什么要在服务方法被调用时引发这个事件呢?

    想一想,我们以上代码是与UI分离的,也就是说,与UI分离是一种很好的编程方法,这样在修改维护时不会搞得乱七八糟。但是,我们都知道世间万物皆为阴阳所生,所以才有太极生两仪,两仪生四象,四象成八卦。而阴与阳是统一的,阴中有阳,阳在有阴。

    我们的应用程序的UI就是阳,而业务逻辑就是阴,所以编程就是这么简单——阴阳互动。为了完成阴中有阳的功能,我们要想办法让这些代码与窗口上的控件互动,当然方法很多,也相当灵活。使用事件是比较好的。

    于是,在服务类中定义一个事件,而事件的处理方法在主窗口类中定义,这样一来,阳与阴之间就有了一个可以相通的渠道。

    为了使用访问方便,在窗口类中定义的处理事件的方法使用静态方法,静态方法的好处在于,它不基于对象,而是基于类的,你去到哪里都可以访问,它是全球化的。

    这时候有人会问了,静态方法不能访问类对象的成员,那么这个静态方法又如何与窗体上的控件互动呢?技巧都是拿来用的。有了静态方法,难道我不能在窗口类中定义一个保存当前类实例的静态变量吗?

    比如,我这个窗口的类名为FormMain,我只要在FormMain里面定义一个static FormMain CurrentForm = null;就完事了,这样不就可以在静态方法中访问了吗?

    只要在FormMain的构造函数中赋值就行了,CurrentForm = this;

    比如本例的代码:

            #region 静态成员
            static Form1 CurrentInstance = null;
            public static void GetMessageCallBack(object sender, ServiceDef.MessageReceiveEventArgs e)
            {
                if (CurrentInstance != null)
                {
                    CurrentInstance.AddMessageToListBox(e.MessageText);
                }
            }
            #endregion
    
            public Form1()
            {
    
                InitializeComponent();
                CurrentInstance = this;
    …………


    你看,这就成了。

    然后当然是定义服务器了,这里我们只有一个终结点,就是上面的IService,所以不用基址了,因为我们也不需要引用服务,直接利用ChannelFactory就行了。

            #region 与服操作有关
            ServiceHost host = null;
    
            /// <summary>
            /// 启动服务
            /// </summary>
            /// <param name="port">监听端口</param>
            void OpenService(int port)
            {
                host = new ServiceHost(typeof(ServiceDef.MyChatService));
                NetTcpBinding binding = new NetTcpBinding();
                binding.Security.Mode = SecurityMode.None;
                host.AddServiceEndpoint(typeof(ServiceDef.IService), binding, "net.tcp://" + Dns.GetHostName() + ":"+ port.ToString() + "/chatsvc/");
                host.Opened += host_Opened;
                host.Closed += host_Closed;
                try
                {
                    host.Open();
                }
                catch (Exception ex)
                {
                    ShowMessage(ex.Message);
                }
            }
    
            void host_Closed(object sender, EventArgs e)
            {
                ShowMessage("服务已关闭。");
            }
    
            void host_Opened(object sender, EventArgs e)
            {
                ShowMessage("服务已启动。");
            }
    
            /// <summary>
            /// 关闭服务
            /// </summary>
            void CloseService()
            {
                if (host != null)
                {
                    host.Close();
                    host.Opened -= host_Opened;
                    host.Closed -= host_Closed;
                }
            }
    
            #endregion
    

    好了,接下来就是发送消息,其实就是调用服务方法IService.SendMessage,这里我们只用ChannelFactory<TChannel>工厂来生产一个IService通道,而后直接调用就可以了,就不必引用服务,也不用生成什么WSDL文件,也不考虑SOAP版本了。

                // 发送消息,即调用服务
                NetTcpBinding binding =new NetTcpBinding();
                binding.Security.Mode = SecurityMode.None;
                
                try
                {
                    ServiceDef.IService ep = ChannelFactory<ServiceDef.IService>.CreateChannel(binding,new EndpointAddress("net.tcp://" + txtSvrHostName.Text + ":" + rmPort.ToString() + "/chatsvc/"));
                    ep.SendMessage(txtSendMessage.Text);
                    txtSendMessage.Clear();
                }
                catch (Exception ex)
                {
                    ShowMessage(ex.Message);
                }
    


    哈哈,是不是很简单,而且,你也可以想到,如果用WCF来做文件传输,比如PC与手机上的文件传送,是不是很方便呢?也不必担心TCP粘包问题。

    源代码我随后上传。

  • 相关阅读:
    HDOJ 1846 Brave Game
    并查集模板
    HDU 2102 A计划
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    POJ 1321 棋盘问题
    CF 999 C.Alphabetic Removals
    CF 999 B. Reversing Encryption
    string的基础用法
    51nod 1267 4个数和为0
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3000628.html
Copyright © 2020-2023  润新知