本文將詳細講解用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,整個過程就這樣結束了。