C#-Winform为宿主实现WCF通讯简单示例

WCF是微软弄的一组数据通信的开发接口,即windows通讯接口。和TCP类似需要IP地址和端口号,服务端提供一些函数接口,客户端可进行调用,支持多个客户端。不太懂理论,直接看应用吧。

我的Winform程序A中定义了一个学校(School)类,其中学生(Student)的身高体重不断变化,并可新增或删除学生。现在我新建另一个Winform程序B,要实现以下简单功能:

1.B和A进行通讯连接,连接建立后程序A实例化一个School;

2.B可以发命令给A,对学生进行新增和删除工作;

3.B界面的文本框可以实时刷新A中School的学生的动态(身高,体重等变化);下面通过WCF实现:

首先建立winform程序A,右击项目添加类->WCF服务->命名->点击保存;

17880a7391a16a65a99c8da0d9764e80.png

此时会生成两个cs文件,IHostInterface.cs和HostInterface.cs,其中IHostInterface.cs声明了函数接口,HostInterface中是函数的具体实现函数,将新增/删除/监控等功能函数分别在两个文件声明和实现。

IHostInterface接口:

namespace Host.WCFInterface
{
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IHostInterface”。
    [ServiceContract]
    public interface IHostInterface
    {
        [OperationContract]
        void Hello(string msg);


        [OperationContract]
        int Add(Children c);


        [OperationContract]
        int Del(int num);


        [OperationContract]
        string Monitor();
    }
}

HostInterface函数实现:

namespace Host.WCFInterface
{
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的类名“HostInterface”。
    public class HostInterface : IHostInterface
    {
        School sch;
        string name;
        //建立连接,实例化School
        public void Hello(string msg)
        {
            name = msg;
            sch = new School(msg);
        }
        //添加学生
        public int Add(Children c)
        {
            sch.AddStudents(c);
            return sch.Count;
        }
        //删除学生
        public int Del(int num)
        {
            sch.DelStudents(num);
            return sch.Count;
        }
        //获取实时信息
        public string Monitor()
        {
            return sch.Description;
        }
    }
}

接口和函数写好后,要对WCF服务进行配置,打开app.config,如图:

c1ee86bd0686dddf57a204ec84ac8145.png

首先是IP地址,图中1和2不一致,1是基地址(可写为localhost也可指定IP),2是客户端访问接口地址。includeexceptiondetailinFault设置为true,可以捕获通道异常,下面的是一些时长和接受缓存设置。

配置完成后,下面实现启动服务,首先添加引用:

using System.ServiceModel;
using System.ServiceModel.Description;

开启服务:

ServiceHost server = new ServiceHost(typeof(HostInterface));
server.Open();

服务端已经完成,运行程序A,新建客户端程序B,右击B项目,点击添加服务引用,在右图中输入服务地址(上述地址2),->转到->命名->确定;

b8fbe1a137ee50cf83200185aaee5ea9.png  -> 5b7c987d93aaf3490f095c4028822843.png

如图表示添加成功,然后右击选择配置服务引用:

0a0cc4586895d572b6d2854578ff6881.png  ->  eb327dd52add67382ceb8851905fdc84.png

若接口中需要传递List类型,则按照图中选择,点击确定。

47909e7db34380df3e1a9c5a1fbaee8f.png

引用添加并配置成功,打开客户端程序的app.config对通讯进行配置:

c41e3daa8a5c84febf9efced0f388835.png

接下来程序B可以调用A的接口函数并实现功能:

HostInterfaceClient client;
        public Comm()
        {
            Thread th = new Thread(Monitor);
            th.IsBackground = true;
            th.Start();
        }
        void Monitor()
        {
            while (true)
            {
                Thread.Sleep(1000);
                if (!IsConnected)
                    continue;
                try
                {
                    ShowMsg(client.Monitor());
                }
                catch (Exception ex)
                {
                    ShowMsg(ex.Message);
                    IsConnected = false;
                }
            }
        }
        public void Register()
        {
            if (IsConnected)
                return;
            try
            {
                client = new HostInterfaceClient();
                client.Hello("Demo");
                IsConnected = true;
            }
            catch
            {
                IsConnected = false;
            }
        }
        void GetChannel()
        { 


        }
        public int Add(Children c)
        {
            if (!IsConnected)
                return 0;
            try
            {
                return client.Add(c);
            }
            catch
            {
                IsConnected = false;
                return 0;
            }
        }
        public int Del(int num)
        {
            if (!IsConnected)
                return 0;
            try
            {
                return client.Del(num);
            }
            catch
            {
                IsConnected = false;
                return 0;
            }
        }

当程序A提供的接口函数增加减少或变化时,需要在客户端程序B进行服务更新,如图:

b68a22de1b0565b40dd83f92ac39a23f.png2e4313cb634bc4c6854d05d334c1bb27.png

注意,服务引用的添加、配置和更新要在服务启动即程序A运行的情况下进行。

验证通讯功能函数调用,数据刷新正常:

5df89a5aba936f154ee9bbc407c1d12a.png

现在我觉得通过app.config对参数进行配置不方便,想通过代码直接配置,可以将appCofig中的配置代码注释掉,如下编写代码:

服务端:

public Form1()
        {
            InitializeComponent();
            ThreadPool.QueueUserWorkItem(new WaitCallback(InitHost),true);
        }
        void InitHost(object flag)
        {
            try
            {
                ServiceHost server = new ServiceHost(typeof(HostInterface));
                NetTcpBinding binding = new NetTcpBinding();
                binding.Security.Mode = SecurityMode.None;
                binding.TransferMode = TransferMode.Buffered;
                binding.MaxBufferSize = int.MaxValue;
                binding.MaxReceivedMessageSize = int.MaxValue;
                string baseAdd = "net.tcp://123.45.67.89:123/HostInterface/";
                string pointAdd = "net.tcp://123.45.67.89:1234/HostInterface/";
                server.AddServiceEndpoint(typeof(IHostInterface), binding, baseAdd);
                if (server.Description.Behaviors.Find<ServiceMetadataBehavior>() == null)
                {
                    ServiceMetadataBehavior metadata = new ServiceMetadataBehavior();
                    server.Description.Behaviors.Add(metadata);
                }
                server.Description.Behaviors.Find<ServiceMetadataBehavior>().HttpGetEnabled = false;
                System.ServiceModel.Channels.Binding bind = MetadataExchangeBindings.CreateMexTcpBinding();
                server.AddServiceEndpoint(typeof(IMetadataExchange), bind, pointAdd);
                if (server.Description.Behaviors.Find<ServiceBehaviorAttribute>() == null)
                {
                    ServiceBehaviorAttribute attr = new ServiceBehaviorAttribute();
                    server.Description.Behaviors.Add(attr);
                }
                server.Description.Behaviors.Find<ServiceBehaviorAttribute>().IncludeExceptionDetailInFaults = true;
                server.Open();
            }
            catch
            {
                MessageBox.Show("open Port Fail!");
                Application.Exit();
            }
        }

客户端(使用IP地址1):

public void Register()
        {
            if (IsConnected)
                return;
            try
            {
                EndpointAddress add = new EndpointAddress("net.tcp://123.45.67.89:123/HostInterface/");
                NetTcpBinding binding = new NetTcpBinding();
                binding.OpenTimeout = TimeSpan.FromSeconds(10);
                binding.Security.Mode = SecurityMode.None;
                binding.SendTimeout = TimeSpan.FromSeconds(30);
                binding.TransferMode = TransferMode.Buffered;
                binding.MaxBufferSize = int.MaxValue;
                binding.MaxReceivedMessageSize = int.MaxValue;
                client = new HostInterfaceClient(binding, add);
                client.Hello("Demo");
                IsConnected = true;
            }
            catch
            {
                IsConnected = false;
            }
        }

由于我逻辑变动频繁,需要不停的变更接口函数,每次更新服务引用繁琐,还要在服务端A启动时进行。我想把接口函数封装到DLL中,每次接口变更,我只需更新DLL,然后在服务端实现和客户端调用即可。操作如下:

1.将Interface接口函数类移动到DLL中,注意:通讯用到的类如Student,也要移动到DLL中;

using System.ServiceModel;


namespace DLLDemo
{
    // 注意: 使用“重构”菜单上的“重命名”命令,可以同时更改代码和配置文件中的接口名“IHostInterface”。
    [ServiceContract]
    public interface IHostInterface
    {
        [OperationContract]
        void Hello(string msg);


        [OperationContract]
        int Add(Children c);


        [OperationContract]
        int Del(int num);


        [OperationContract]
        string Monitor();
    }
    public class Children
    {
        Random r = new Random();
        public int number = 2022001;
        public string name = "na";
        public int age = 1;
        public bool isBoy = true;
        public int height = 100;//cm
        public int weight = 25;//kg
        public Children()
        {
            age = r.Next(6, 9);
            isBoy = r.Next(1, 100) % 2 == 0;
            height = r.Next(80, 110);
            weight = r.Next(25, 40);
        }
    }
}

2.将服务端程序A中的IHostInterface.cs删除,添加DLLDemo的引用;

3.将客户端程序B中的服务引用HostService删除,添加DLLDemo的引用,修改函数,使用工厂创建通道:

IHostInterface client;
        public void Register()
        {
            if (IsConnected)
                return;
            try
            {
                EndpointAddress add = new EndpointAddress("net.tcp://123.45.67.89:123/HostInterface/");
                NetTcpBinding binding = new NetTcpBinding();
                binding.OpenTimeout = TimeSpan.FromSeconds(10);
                binding.Security.Mode = SecurityMode.None;
                binding.SendTimeout = TimeSpan.FromSeconds(30);
                binding.TransferMode = TransferMode.Buffered;
                binding.MaxBufferSize = int.MaxValue;
                binding.MaxReceivedMessageSize = int.MaxValue;
                ChannelFactory<IHostInterface> factory = new ChannelFactory<IHostInterface>(binding,add);
                client = factory.CreateChannel();
                client.Hello("Demo");
                IsConnected = true;
            }
            catch
            {
                IsConnected = false;
            }
        }

我想在服务端对通道状态进行监控,异常或者断开进行处理,在服务端HostInterce类中添加函数如下:

public HostInterface()
        {
            OperationContext.Current.Channel.Closed += clientClosed;
            OperationContext.Current.Channel.Faulted += clientFaulted;
        }
        void clientClosed(object sender, EventArgs e)
        {
            string ceshi = name;
            //通道关闭,进行处理...
        }
        void clientFaulted(object sender, EventArgs e)
        {
            string ceshi = name;
            //通道异常,进行处理...
        }

我想在客户端添加主动与服务端断开的功能,在客户端新增CloseChannel函数,代码如下:

((IClientChannel)client).Close();

示例到此就结束了,日常使用不限于Winform之间,Winform-MVC,MVC-MVC,可自由搭配。

备注:

1.使用中通道会发生超时导致异常断开,例如电脑时钟跳变,可以修改超时时长参数,也可使用短连接方式。

2.Host服务最好不要放在主线程中开启。

3.不可超过十分钟无通讯,否则通道会自动关闭并产生异常。

4.MaxBufferSize设置,否则回传数据过大会产生异常。

5.includeExceptionDetailInFaults要设置为True,以便于捕获异常。

6.若传递List<T>类型作为函数参数,注意使用过程中不要修改List,否则会产生异常;

原文链接:https://www.cnblogs.com/cfsl/p/16922830.html

- EOF -

技术群:添加小编微信dotnet999

公众号:dotnet讲堂

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WCF全面解析(套装上下册)》由蒋金楠所著,是作者多年潜心研究WCF技术的心血之作,也是这些年来从事WCF开发的经验总结。书如其名,此书涵盖了WCF几乎所有的知识点,并对其底层框架进行了“庖丁解牛”式的剖析,力求将WCF的整个运行机制完整而清晰地呈现在读者面前。 《WCF全面解析(套装上下册)》上册的前四章在对WCF进行总体介绍的基础上,对构成终结点的三要素(地址、绑定和契约)进行了系统说明;随后的两章则着重剖析序列化和消息编码在WCF中的实现;第7、8章讲述了在服务寄宿和操作调用过程中,WCF的服务端和客户端框架分别为我们做了什么;第9、10章将介绍的重点落在实例化、会话和REST服务上面;在最后一章中采用WCF构建了一个具体的电子商务网站VM,它将指导你如何将理论应用于实践。 《WCF全面解析(套装上下册)》的下册主要涉及一些所谓的“高级”话题,主要包括如何在分布式环境中处理异常(第1章);元数据的导入与导出、发布与获取如何实现(第2章);如何利用WCF对事务的支持将分布式事务引入服务(第3章);如何利用并发与限流机制提高服务的吞吐量和可用性(第4章);如何利用可靠会话机制确保消息的“使命必达”(第5章);如何利用队列服务提供离线通信的支持(第6章);第7、8章主要涉及安全的相关内容,包括传输安全、授权与审核;第9章全景展示WCF服务端和客户端的运行时框架,以及在此基础上的所有扩展可能;最后一章为你带来WCF4.0几个独立的新特性。 编辑推荐 《WCF全面解析(套装上下册)》不仅适合尚未接触过WCF,希望尽快入门并进行深入研究的开发人员使用,同样也适合对WCF有一定了解的开发设计人员和架构师阅读。相信不同层次的读者都能从此书中找到自己希望了解的部分。 《WCF全面解析(套装上下册)》的内容不仅适合尚未接触过WCF,希望尽快入门并进行深入研究的开发人员,同样适合对WCF具有一定了解的开发设计人员和架构师。相信不同层次的读者都能从本书中找到自己希望了解的部分。阅读本书的读者需要对.NET,包括对C#和.NET Framework具有一定的了解。如果读者具备了DCOM、Enterprise Library Service、.NET Remoting、Web Service、MSMQ及SOA相关的基础,对阅读此书尽快掌握WCF将大有裨益。 名人推荐我经历了COM时代,一直把Don Box的《COMM本质论》奉为我的指路明灯。能把SOA机理和WCF这种特定厂商实现的技术讲得如《COM本质论》一样完美透彻的,那必属Artech这本经过自己深研、实践而著的心血结晶——《WCF全面解析》。如果你想成为SOA和WCF方面的专家,那么这本书就是你的最好法宝。想想你作为专家而获得的回报,那么你对这本书购买所付出的,简直是太值了。 ——《走出软件作坊》 作者 明源软件CTO 阿朱 首先,金楠是—位工作在一线的优秀的WCF技术人员,这符合我对阅读技术图书的第一个要求和期待。其次,金楠的写作文笔、专业责任也给人以充分信任,这在金楠的文字中读者可以体会。这本《WCF全面解析》全面剖析了构建WCF应用所需要的各方面技术,剥丝抽茧,由浅入深,也是我非常欣赏的技术讲述方式。我相信《WCF全面解析》—书是搞WCF朋友的案头必备。 ——祝成科技与Boolan.com创始人.NET技术专家 李建忠 知识全面、论述准确、逻辑严密是本书的特点。这是一本各层次开发人员都可以从中受益的书:对于初、中级开发人员,它可以帮助你获得WCF全方位的知识,系统地梳理WCF的知识结构,提升动手实践能力;对于高级开发人员,它既可以有效弥补你WCF相关知识中的盲点,又可以让你在自己熟悉的知识点上领略作者的看法和理解。 ——资深架构师 曲春雨 作者简介蒋金楠,网名Artech,现就职于某知名软件公司担任高级软件顾问。连续5届微软MVP(最有价值专家),同时也是少数的双料MVP(Solutions Architecture+Connected System)之一。国内较早接触WCF的人之一,2007年2月起在个人博客(http://www.cnblogs.com/artech)上发表超过两百篇深入介绍WCF的文章,成为了目前国内WCF在线资料的主要来源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值