Ejemplo mínimo de canalización con nombre WCF

90

Estoy buscando un ejemplo mínimo de WCF Named Pipes (espero dos aplicaciones mínimas, servidor y cliente, que pueden comunicarse a través de una canalización con nombre).

Microsoft tiene el brillante artículo Tutorial de introducción que describe WCF a través de HTTP, y estoy buscando algo similar sobre WCF y canalizaciones con nombre.

Encontré varias publicaciones en Internet, pero son un poco "avanzadas". Necesito algo mínimo, solo una funcionalidad obligatoria, para poder agregar mi código y hacer que la aplicación funcione.

¿Cómo reemplazo eso para usar una tubería con nombre?

<endpoint address="http://localhost:8000/ServiceModelSamples/Service/CalculatorService"
    binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICalculator"
    contract="ICalculator" name="WSHttpBinding_ICalculator">
    <identity>
        <userPrincipalName value="OlegPc\Oleg" />
    </identity>
</endpoint>

¿Cómo reemplazo eso para usar una tubería con nombre?

// Step 1 of the address configuration procedure: Create a URI to serve as the base address.
Uri baseAddress = new Uri("http://localhost:8000/ServiceModelSamples/Service");

// Step 2 of the hosting procedure: Create ServiceHost
ServiceHost selfHost = new ServiceHost(typeof(CalculatorService), baseAddress);

try
{
    // Step 3 of the hosting procedure: Add a service endpoint.
    selfHost.AddServiceEndpoint(
        typeof(ICalculator),
        new WSHttpBinding(),
        "CalculatorService");

    // Step 4 of the hosting procedure: Enable metadata exchange.
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
    smb.HttpGetEnabled = true;
    selfHost.Description.Behaviors.Add(smb);

    // Step 5 of the hosting procedure: Start (and then stop) the service.
    selfHost.Open();
    Console.WriteLine("The service is ready.");
    Console.WriteLine("Press <ENTER> to terminate service.");
    Console.WriteLine();
    Console.ReadLine();

    // Close the ServiceHostBase to shutdown the service.
    selfHost.Close();
}
catch (CommunicationException ce)
{
    Console.WriteLine("An exception occurred: {0}", ce.Message);
    selfHost.Abort();
}

¿Cómo genero un cliente para usar una tubería con nombre?

javapowered
fuente
1
¿Ha mirado stackoverflow.com/questions/184878/… ?
Christoph

Respuestas:

80

Acabo de encontrar este excelente pequeño tutorial . enlace roto ( versión en caché )

También seguí el tutorial de Microsoft, que es bueno, pero solo necesitaba tuberías.

Como puede ver, no necesita archivos de configuración y todo ese desorden.

Por cierto, usa HTTP y canalizaciones. Simplemente elimine todas las líneas de código relacionadas con HTTP y obtendrá un ejemplo de tubería pura.

Juan
fuente
2
¡Gracias! Además, cuando intente crear un servicio que utilice web.config para su configuración en lugar de una configuración codificada, consulte este ejemplo de microsoft: msdn.microsoft.com/en-us/library/ms752253.aspx
Nullius
3
El enlace no funciona, ¿el tutorial está en otro lugar?
user1069816
Pasé un rato tratando de averiguar por qué "se terminó la tubería". Aquí está mi solución en eso, espero que ayude: stackoverflow.com/a/49075797/385273
Ben
62

Prueba esto.

Aquí está la parte del servicio.

[ServiceContract]
public interface IService
{
    [OperationContract]
    void  HelloWorld();
}

public class Service : IService
{
    public void HelloWorld()
    {
        //Hello World
    }
}

Aquí está el proxy

public class ServiceProxy : ClientBase<IService>
{
    public ServiceProxy()
        : base(new ServiceEndpoint(ContractDescription.GetContract(typeof(IService)),
            new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/MyAppNameThatNobodyElseWillUse/helloservice")))
    {

    }
    public void InvokeHelloWorld()
    {
        Channel.HelloWorld();
    }
}

Y aquí está la parte de alojamiento del servicio.

var serviceHost = new ServiceHost
        (typeof(Service), new Uri[] { new Uri("net.pipe://localhost/MyAppNameThatNobodyElseWillUse") });
    serviceHost.AddServiceEndpoint(typeof(IService), new NetNamedPipeBinding(), "helloservice");
    serviceHost.Open();

    Console.WriteLine("Service started. Available in following endpoints");
    foreach (var serviceEndpoint in serviceHost.Description.Endpoints)
    {
        Console.WriteLine(serviceEndpoint.ListenUri.AbsoluteUri);
    }
Anuraj
fuente
Esto puede funcionar, pero no es tan flexible como simplemente editar los archivos app.config para el cliente y el servidor ...
Alan S
9
Agradable, ya que a menudo no se desea exponer los detalles de la aplicación a través de archivos app.config.
Frank Hileman
14
Este es un ejemplo maravilloso, sin embargo, nunca use una dirección base de solo net.pipe: // localhost /. Si lo hace y la máquina tiene algún otro programa que también use net.pipe: // localhost /, ServiceHost lanzará una excepción cuando lo abra. En su lugar, use algo único como net.pipe: // localhost / MyAppNameThatNobodyElseWillUse. ¡Espero que esto ayude a alguien más a ahorrar tiempo y frustración!
Doug Clutter
Esta solución funciona bien. Particularmente para endpoints internos donde no es necesario tener una referencia de servicio en la configuración. Simplemente mantenga los contratos, simplemente las definiciones de interfaz, en su propio ensamblado y quizás la dirección en config. No es tan probable que la unión cambie.
Rob Von Nesselrode
2
Necesitaba agregar /helloserviceal final de la dirección del punto final en el proxy.
Mormegil
14

Vea mi ejemplo de Echo altamente simplificado : está diseñado para usar la comunicación HTTP básica, pero se puede modificar fácilmente para usar canalizaciones con nombre editando los archivos app.config para el cliente y el servidor. Realice los siguientes cambios:

Edite el archivo app.config del servidor , quitando o comentando la entrada http baseAddress y agregando una nueva entrada baseAddress para la tubería nombrada (llamada net.pipe ). Además, si no tiene la intención de usar HTTP para un protocolo de comunicación, asegúrese de que serviceMetadata y serviceDebug estén comentados o eliminados:

<configuration>
    <system.serviceModel>
        <services>
            <service name="com.aschneider.examples.wcf.services.EchoService">
                <host>
                    <baseAddresses>
                        <add baseAddress="net.pipe://localhost/EchoService"/>
                    </baseAddresses>
                </host>
            </service>
        </services>
        <behaviors>
            <serviceBehaviors></serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

Edite el archivo app.config del cliente para que el basicHttpBinding sea ​​comentado o eliminado y se agregue una entrada netNamedPipeBinding . También necesitará cambiar la entrada del punto final para usar la tubería:

<configuration>
    <system.serviceModel>
        <bindings>
            <netNamedPipeBinding>
                <binding name="NetNamedPipeBinding_IEchoService"/>
            </netNamedPipeBinding>
        </bindings>
        <client>
            <endpoint address              = "net.pipe://localhost/EchoService"
                      binding              = "netNamedPipeBinding"
                      bindingConfiguration = "NetNamedPipeBinding_IEchoService"
                      contract             = "EchoServiceReference.IEchoService"
                      name                 = "NetNamedPipeBinding_IEchoService"/>
        </client>
    </system.serviceModel>
</configuration>

El ejemplo anterior solo se ejecutará con canalizaciones con nombre, pero nada le impide utilizar varios protocolos para ejecutar su servicio. AFAIK, debería poder hacer que un servidor ejecute un servicio utilizando canalizaciones con nombre y HTTP (así como otros protocolos).

Además, el enlace en el archivo app.config del cliente está muy simplificado. Hay muchos parámetros diferentes que puede ajustar, además de especificar la baseAddress ...

Alan S
fuente
5
Los enlaces ahora están muertos.
Chris Weber
2

Creé este ejemplo simple a partir de diferentes resultados de búsqueda en Internet.

public static ServiceHost CreateServiceHost(Type serviceInterface, Type implementation)
{
  //Create base address
  string baseAddress = "net.pipe://localhost/MyService";

  ServiceHost serviceHost = new ServiceHost(implementation, new Uri(baseAddress));

  //Net named pipe
  NetNamedPipeBinding binding = new NetNamedPipeBinding { MaxReceivedMessageSize = 2147483647 };
  serviceHost.AddServiceEndpoint(serviceInterface, binding, baseAddress);

  //MEX - Meta data exchange
  ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
  serviceHost.Description.Behaviors.Add(behavior);
  serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexNamedPipeBinding(), baseAddress + "/mex/");

  return serviceHost;
}

Usando el URI anterior, puedo agregar una referencia en mi cliente al servicio web.

Rahbek
fuente
-2

Encontré este sitio realmente útil, y el proyecto de ejemplo se ejecuta sin ningún ajuste: https://dotnet-experience.blogspot.com/2012/02/inter-process-duplex-communication-with.html

No olvide habilitar la compatibilidad con Named Pipe en las características de Windows. Este artículo tiene algunas buenas capturas de pantalla a ese efecto en la respuesta principal: WCF Named Pipe en Windows Service usando App.Config

El proyecto al que se hace referencia en la solución aceptada no se ejecuta tal cual en mi PC. Probé algunas correcciones en app.config pero aún obtengo la siguiente excepción:

System.InvalidOperationException: 'Service' WpfWcfNamedPipeBinding.NamedPipeBindingService 'no tiene puntos finales de aplicación (no infraestructura). Esto puede deberse a que no se encontró ningún archivo de configuración para su aplicación, a que no se pudo encontrar ningún elemento de servicio que coincida con el nombre del servicio en el archivo de configuración, o a que no se definieron puntos finales en el elemento de servicio '.

VoteCoffee
fuente