用C#基于WCF创建TCP的Service供Client端调用
本文将详细讲解用C#基于WCF创建TCP的Service供Client端调用的详细过程
1):首先创建一个Windows Service的工程
2):生成的代码工程结构如下所示
3):我们将Service1改名为MainService
4): 添加一个Interface来定义Service的契约
4.1):截图如下所示
4.2):IOrderService.cs的代码如下所示
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; using System.Threading.Tasks; namespace EricSunService { [ServiceContract] interface IOrderService { [OperationContract] [FaultContract(typeof(ServiceFault))] AccountLoginResponse AccountLogin(AccountLoginRequest request); [OperationContract] [FaultContract(typeof(ServiceFault))] AccountTopUpResponse AccountTopUp(AccountTopUpRequest request); } [DataContract] public class ServiceFault { [DataMember] public string CorrelationId { get; set; } [DataMember] public string Message { get; set; } [DataMember] public string Address { get; set; } } }
5):然后添加其他的类实现对应的Service,并且实现对Service的Host
5.1):最终的代码工程截图如下所示(这里的EricSunData工程是用于数据类型的定义,为了更好的逻辑结构分层,这里我们主要以AccountLogin.cs中所实现的OrderService进行讲解)
5.2):AccountLogin.cs的代码如下所示(实现IOrderService中的部分接口)
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace EricSunService { public partial class OrderService : IOrderService { public AccountLoginResponse AccountLogin(AccountLoginRequest request) { // do some logic with account info AccountLoginResponse loginResponse = new AccountLoginResponse() { AccountBalance = 10000000.00, Status = new AccountLoginStatus() }; return loginResponse; } } [DataContract] public class AccountLoginRequest { [DataMember] public string Name { get; set; } [DataMember] public string Password { get; set; } } [DataContract] public class AccountLoginResponse { [DataMember] public double AccountBalance { get; set; } [DataMember] public AccountLoginStatus Status { get; set; } } public enum AccountLoginStatus { NoError = 0, InvalidAccountInfo // Invalid Account Info } }
5.3):MainService的代码如下所示 (进行对Service的Host)
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Linq; using System.ServiceModel; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace EricSunService { public partial class MainService : ServiceBase { private ServiceHost _orderService; public MainService() { InitializeComponent(); } protected override void OnStart(string[] args) { _orderService = new ServiceHost(typeof(OrderService)); _orderService.Open(); } protected override void OnStop() { _orderService.Close(); } } }
5.4):Program.cs的代码如下所示 (.exe运行时主入口)
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace EricSunService { static class Program { /// <summary> /// The main entry point for the application. /// </summary> static void Main() { #if DEBUG ServiceHost host = new ServiceHost(typeof(OrderService)); host.Open(); System.Threading.Thread.Sleep(System.Threading.Timeout.Infinite); #else ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new MainService() }; ServiceBase.Run(ServicesToRun); #endif } } }
6):运行Service时的可能错误以及App.config的配置
6.1):当我们build真个solution之后,到对应的debug目录去运行对应的EricSunService.exe文件时,有可能会出现如下错误,为了解决如下的错误才有了5.4中写法
6.2):App.config文件的配置信息,是对WCF框架下暴露Service的endpoint(ABC)的一个详细配置
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" /> </startup> <system.serviceModel> <behaviors> <serviceBehaviors> <behavior name="OrderServiceBehavior"> <serviceMetadata httpGetEnabled="false" /> <serviceDebug /> </behavior> </serviceBehaviors> </behaviors> <services> <service behaviorConfiguration="OrderServiceBehavior" name="EricSunService.OrderService"> <host> <baseAddresses> <add baseAddress="net.tcp://localhost:3434/" /> </baseAddresses> </host> <endpoint address="" binding="netTcpBinding" bindingConfiguration="NetTcpBindingConfig" contract="EricSunService.IOrderService"/> <endpoint address="mex" binding="mexTcpBinding" bindingConfiguration="" contract="IMetadataExchange" /> </service> </services> <bindings> <netTcpBinding> <binding name="NetTcpBindingConfig"> <security mode="None" /> </binding> </netTcpBinding> </bindings> </system.serviceModel> </configuration>
7):创建一个Asp.Net MVC 的工程作为Client端去调用所提供的Service,之后是添加对OrderService的引用,如下图所示
8):在EricSunService.exe运行起来的状态下,去update此OrderServiceReference,如下图所示
9):点击Show All Files之后会看到如下详细的工程文件信息
10):同时我们发现了如下图的错误信息
11):为了解决这个错误信息,请按下图的步骤进行操作
11.1):鼠标右键点击OrderServiceReference后选择Config Service Reference
11.2):取消对Reuse types in referenced assemblies的勾选
11.3):点击上图中的OK按钮之后,生成了Service所对应Data的详细信息,如下图所示
11.4):最终的工程结构如下图所示
12):Service的引用添加完毕之后,就可以对Service进行调用了,我们这里选择的是ChannelFactory的方式,详细代码如下所示
12.1):OrderServiceClientFactory.cs
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Web; using EricSunWeb.OrderServiceReference; namespace EricSunWeb.Business { public static class OrderServiceClientFactory { private static readonly object CRITICAL_SECTION = new object(); private static ChannelFactory<IOrderServiceChannel> s_ChannelFactory = null; public static IOrderServiceChannel CreateClient() { if (s_ChannelFactory == null || s_ChannelFactory.State == CommunicationState.Faulted) { lock (CRITICAL_SECTION) { if (s_ChannelFactory == null) { s_ChannelFactory = new ChannelFactory<IOrderServiceChannel>("NetTcpBinding_IOrderService"); } else if (s_ChannelFactory.State == CommunicationState.Faulted) { s_ChannelFactory.Abort(); s_ChannelFactory = new ChannelFactory<IOrderServiceChannel>("NetTcpBinding_IOrderService"); } } } return s_ChannelFactory.CreateChannel(); } } }
12.2):OrderManager.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using EricSunWeb.OrderServiceReference; namespace EricSunWeb.Business { public class OrderManager { public void AccountLogin(string name, string password) { var request = new AccountLoginRequest { Name = name, Password = password }; AccountLoginResponse response = null; var client = OrderServiceClientFactory.CreateClient(); response = client.AccountLogin(request); if (response.Status == AccountLoginStatus.NoError) { } else { } } } }
12.3):OrderController.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using EricSunWeb.Business; using EricSunWeb.OrderServiceReference; namespace EricSunWeb.Controllers { public class OrderController : Controller { // // GET: /Order/ public ActionResult Index() { new OrderManager().AccountLogin("EricSun", "password"); return View(); } } }
OK,整个过程就这样结束了。