// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; using System.Collections.ObjectModel; using System.Net.Sockets; namespace Spludlow { public class ServiceModels { public static string Address(string host, string typeName, int portNumber) { if (typeName.EndsWith("*") == true) typeName = typeName.Substring(0, typeName.Length - 1); return "net.tcp://" + host + ":" + portNumber + "/" + typeName; } public static string InterfaceName(Type type) { Type[] interfaces = type.GetInterfaces(); if (interfaces.Length == 0) throw new ApplicationException("ServiceModels InterfaceName; Type does not have any interface, type: " + type.FullName); if (interfaces.Length > 1) { string warning = "ServiceModels InterfaceName; Type has more than one interface using first one: "; foreach (Type interfaceType in interfaces) warning += interfaceType.FullName + ", "; Spludlow.Log.Warning(warning); } return interfaces[0].FullName; } public static Type InterfaceType(Type type) { string interfaceTypeName = Spludlow.ServiceModels.InterfaceName(type); foreach (Type interfaceType in type.GetInterfaces()) { if (interfaceType.FullName == interfaceTypeName) return interfaceType; } throw new ApplicationException("Interface not found in Type: " + type.FullName + ", " + interfaceTypeName); } public static ServiceHost StartServer(string assemblyName, string typeName, int portNumber) { bool perCall = false; if (typeName.EndsWith("*") == true) { typeName = typeName.Substring(0, typeName.Length - 1); perCall = true; } Type type = Spludlow.Reflections.FindType(assemblyName, typeName); Type interfaceType = InterfaceType(type); return StartServer(type, interfaceType, portNumber, perCall); } public static ServiceHost StartServer(string key, Type type, bool perCall) { string address = Spludlow.Config.RemotingAddress(key); int lastColon = address.LastIndexOf(":"); int lastSlash = address.LastIndexOf("/"); if (lastColon == -1 || lastSlash == -1) throw new ApplicationException("ServiceModels, StartServer; Bad address:\t" + address); int portNumber = Int32.Parse(address.Substring(lastColon + 1, lastSlash - (lastColon + 1))); Type interfaceType = InterfaceType(type); return StartServer(type, interfaceType, portNumber, perCall); } public static ServiceHost StartServer(Type classType, Type interfaceType, int portNumber, bool perCall) { string address = Address(Environment.MachineName, classType.FullName, portNumber); try { ServiceHost serviceHost = new ServiceHost(classType); NetTcpBinding binding = new NetTcpBinding(SecurityMode.None); serviceHost.AddServiceEndpoint(interfaceType, binding, address); ServiceBehaviorAttribute behavior = serviceHost.Description.Behaviors.Find(); if (perCall == false) behavior.InstanceContextMode = InstanceContextMode.Single; else behavior.InstanceContextMode = InstanceContextMode.PerCall; // Send exceptions to client ServiceDebugBehavior debugBehavior = serviceHost.Description.Behaviors.Find(); if (debugBehavior == null) serviceHost.Description.Behaviors.Add(new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true }); else debugBehavior.IncludeExceptionDetailInFaults = true; // Log Errors in server serviceHost.Description.Behaviors.Add(new ExceptionHandlerBehavior()); serviceHost.Open(); Spludlow.Log.Report("ServiceModels Started WCF TCP Server: " + address + ", perCall=" + perCall); return serviceHost; } catch (Exception ee) { Spludlow.Log.Error("ServiceModels Starting WCF TCP Server: " + address, ee); throw ee; } } public static void Close(object clientInterface) { IClientChannel clientChannel = (IClientChannel)clientInterface; if (clientChannel.State == CommunicationState.Faulted) clientChannel.Abort(); else clientChannel.Close(); } public static string TcpServerError(int portNumber) { using (TcpClient tcpClient = new TcpClient()) { try { tcpClient.Connect(Environment.MachineName, portNumber); tcpClient.Close(); return null; } catch (SocketException ee) { return ee.SocketErrorCode.ToString(); } } } public void TestChannels() { string address = Spludlow.Config.RemotingAddress("ParallelResults"); NetTcpBinding binding = new NetTcpBinding(SecurityMode.None); EndpointAddress endpointAddress = new EndpointAddress(address); ChannelFactory channelFactory = new ChannelFactory(binding, endpointAddress); Spludlow.Log.Info(channelFactory.State.ToString()); //Spludlow.Log.Info(System.Runtime.Remoting.Channels.ChannelServices.RegisteredChannels.Length.ToString()); //foreach (System.Runtime.Remoting.Channels.IChannel channel in System.Runtime.Remoting.Channels.ChannelServices.RegisteredChannels) //{ // Spludlow.Log.Info(channel.ChannelName); //} } } // want it to stay alive after fault ???? public class ExceptionHandlerBehavior : IServiceBehavior { public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) channelDispatcher.ErrorHandlers.Add(new ServiceErrorHandler()); } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } } public class ServiceErrorHandler : IErrorHandler { public bool HandleError(Exception error) { Spludlow.Log.Error("WCF HandleError", error); return true; // true if Windows Communication Foundation (WCF) should not abort the session (if there is one) } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { // ??? ErrorSignal.FromCurrentContext().Raise(error); //FaultException faultException = new FaultException(error.Message); //MessageFault messageFault = faultException.CreateMessageFault(); //fault = Message.CreateMessage(version, messageFault, faultException.Action); } } }