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服务->命名->点击保存;
此时会生成两个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,如图:
首先是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),->转到->命名->确定;
->
如图表示添加成功,然后右击选择配置服务引用:
->
若接口中需要传递List类型,则按照图中选择,点击确定。
引用添加并配置成功,打开客户端程序的app.config对通讯进行配置:
接下来程序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进行服务更新,如图:
注意,服务引用的添加、配置和更新要在服务启动即程序A运行的情况下进行。
验证通讯功能函数调用,数据刷新正常:
现在我觉得通过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函数,代码如下:
1 | ((IClientChannel)client).Close(); //在正常通讯状态下,客户端使用此方式断开 |
1 2 3 4 5 | var comm = channel as ICommunicationObject; //在通讯发生异常时,使用此方式终止 if (comm != null ) { comm.Abort(); } |
示例到此结束,日常使用不限于Winform之间,Winform-MVC,MVC-MVC,可自由搭配。
备注:
1.使用中通道会发生超时导致异常断开,例如电脑时钟跳变,可以修改超时时长参数,也可使用短连接方式。
2.Host服务最好不要放在主线程中开启。
3.不可超过十分钟无通讯,否则通道会自动关闭并产生异常。
4.MaxBufferSize设置,否则回传数据过大会产生异常。
5.includeExceptionDetailInFaults要设置为True,以便于捕获异常。
6.若传递List<T>类型作为函数参数,注意使用过程中不要修改List,否则会产生异常;
7.接口函数中的可序列化参数必须支持无参构造函数,否则调用时会报错。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)