zoukankan      html  css  js  c++  java
  • 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粘包问题。

    源代码我随后上传。

  • 相关阅读:
    树莓派学习记录
    ESP8266
    城轨列控系统
    VMware+CentOS7学习记录
    眼镜
    调试蜂鸣器驱动电路
    假期作业进度6
    假期作业进度5
    假期作业进度4
    假期作业进度3
  • 原文地址:https://www.cnblogs.com/jiangu66/p/3000628.html
Copyright © 2011-2022 走看看