Comunicaciones entre procesos con netNamedPipeBinding

Recientemente alguien me preguntaba como comunicar dos procesos que ejecutan en la misma máquina de forma eficiente. Esta persona estaba descantando WCF por tratarse de procesos que ejecutan en la misma máquina. ¿pero por qué ?!!!! WCF pone a nuestra disposición un binding especifico para este tipo de comunicaciones – netNamedPipeBinding.

El caso es que esta pregunta me pico la curiosidad … así que he hecho un sencillo programa de prueba. 

El proyecto consta de los siguientes elementos:

  • Un proyecto de tipo libreria de WCF. Donde definiremos la interfaz e implementación del servicio.
  • Una aplicación de consola que servirá como host del servicio.
  • Una segunda aplicación de tipo windows forms que actuará como cliente del servicio.

El explorador de soluciones tendrá el siguiente aspecto:

image

Lo primero que tenemos que hacer es definir el servicio, una interfaz realmente simple con un único método “RealizarProceso”:

namespace WcfServiceLibrary1
{   
    [ServiceContract]
    public interfaceIService
  
{
        [OperationContract]
        string RealizarProceso(int value);
             
    }   
}

La implementación del servicio es también muy simple, tan solo generamos un mensaje en función del parámetro recibido. Únicamente destacar que hemos creado un constructor que recibe un TextWriter – esto permitirá a la aplicación que hospede el servicio enviar la instancia de la consola para visualizar la ejecución del proceso.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.IO;

namespace WcfServiceLibrary1
{    
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] 
    public class Service : IService
    {

        private TextWriter _out;
        
        public Service() {
            _out = null;
        }

        public Service(TextWriter outwriter)
        {           
            _out = outwriter;
        }

        public string RealizarProceso(int value)
        {
            string message = string.Format("Se ha recibido el dato: {0}", value);
            Write(message);
            return message;
        }
        
        protected virtual void Write(string message)
        {
            if (_out != null) {
                _out.WriteLine(message); 
            }
        }

    }
}

Sigue siendo necesario mantener el constructor sin parámetros ya que en caso contrario recibiríamos un error del compilador. Fijémonos que hemos definido el método Write como virtual, esto nos permitirá sobreescribir el método Write en el caso de que utilicemos el constructor sin parámetros.

Bien, ya tenemos nuestro servicio. Ahora tendremos que hospedarlo en una aplicación de consola – por supuesto tendremos que añadir la referencia a la librería del servicio. Esta aplicación host es muy sencilla, y únicamente aloja el servicio a través de una instancia de ServiceHost

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using WcfServiceLibrary1;

namespace host
{
    class Program
    {
        static void Main(string[] args)
        {

            Service servicio = new Service(Console.Out);

            using (ServiceHost serviceHost = new ServiceHost(servicio))
            {
                try
                {                    
                    serviceHost.Open();
                    
                    Console.WriteLine("EL servicio esta funcionando.");
                    Console.WriteLine("Pulsa <ENTER> para finalizar la ejecucion.");
                    Console.ReadLine();
                    
                    serviceHost.Close();
                }
                catch (TimeoutException ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.ReadLine();
                }
                catch (CommunicationException ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.ReadLine();
                }
            }
        }
    }
}

La configuración del servicio es uno de los puntos que mas problemas suele dar, recordar aqui el ABC:

  • address - expone el servicio en net.pipe://localhost/service1
  • binding – netNamedPipeBinding
  • contract – nuestra interfaz, IService

También es importante recordar que el atributo name del servicio debe ser el nombre de la clase que implementa la interfaz!. De este modo el archivo de configuración quedaría del siguiente modo:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>       
        <services>
            <service name="WcfServiceLibrary1.Service">
                <endpoint address="net.pipe://localhost/service1"
                          binding="netNamedPipeBinding"
                          bindingConfiguration=""
                          contract="WcfServiceLibrary1.IService" />
              
            </service>
        </services>
    </system.serviceModel>
</configuration>

Bien ahora solo nos faltaría el cliente del servicio, en nuestro caso una aplicación Windows Forms – a la que añadiremos la referencia al servicio. Para facilitar esta tarea debemos asegurarnos de que el archivo de configuracion del proyecto que tiene el servicio (la libreria dll) se incluya el endpoint de definicion (mex).

<system.serviceModel>    
  <services>
    <service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
      name="WcfServiceLibrary1.Service">        
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:1630/WcfServiceLibrary1" />
        </baseAddresses>
      </host>
    </service>      
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="WcfServiceLibrary1.Service1Behavior">
        <serviceMetadata httpGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="false" />
      </behavior>        
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>

image

Una vez agregada la referencia, el cliente será una simple ventana con un botón que llamará 1000 veces al servicio que hospedado en la aplicación de consola anterior y mostrará el tiempo total invertido en el proceso:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            try
            {
                TestService();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private static void TestService()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            using (ServiceReference1.ServiceClient proxy =
                new WindowsFormsApplication1.ServiceReference1.ServiceClient())
            {
                for (int i = 0; i < 1000; i++)
                {
                    proxy.RealizarProceso(i);
                }
                sw.Stop();
            }
            string message = String.Format("Segundos:{0}, ms:{1}",
                sw.Elapsed.TotalSeconds,
                sw.Elapsed.TotalMilliseconds);
            MessageBox.Show(message);
        }
    }
}

Para calcular el tiempo total de ejecución se utiliza una instancia de Stopwatch – una clase especializada para este tipo de operaciones incluida en System.Diagnostics

La configuración de la aplicación cliente es la siguiente:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>        
        <client>
            <endpoint address="net.pipe://localhost/service1" 
                      binding="netNamedPipeBinding"                  
                      contract="ServiceReference1.IService"
                      name="NetNamedPipeBinding_IService">
                <identity>
                    <dns value="localhost" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Para ejecutar correctamente el proyecto debemos habilitar el incio multiple de proyectos, nos posicionamos sobre la solución, accedemos a propiedades y marcamos “Multiple startup projects” como muestra la imagen.

image

Con esto tenemos preparado nuestro sistema para la ejecución. Por un lado el servicio …

image 

Y la ejecución de nuestro programa con el MessageBox que nos indica que el tiempo total de ejecución ha sido de 275 milisegundos:

image

No es el objetivo de este post evaluar el rendimiento de netNamedPipeBinding, por lo que dejo a cada cual evaluar si este rendimiento es suficiente para sus requerimientos … tan solo pretendo presentar netNamedPipeBinding a aquellos que no lo conocierais y mostrar un ejemplo de como utilizarlo.

Saludos, DJK

Pedro  Herrarte  Sánchez
Comunicaciones entre procesos con netNamedPipeBinding
Pedro Herrarte Sánchez

Pedro Herrarte, es consultor independiente, ofreciendo servicios de consultoría, análisis, desarrollo y formación. Posee mas de diez años de experiencia trabajando para las principales empresas de España. Es especialista en tecnologías .NET, entornos Web (ASP.NET, ASP.NET MVC,jQuery, HTML5), bases de datos (SQL Server y ORACLE) e integración de sistemas. Es experto en desarrollo (C#, VB.Net, T-SQL, PL/SQL, , ASP, CGI , C, Pro*C, Java, Essbase, Vignette, PowerBuilder y Visual Basic ...) y bases de datos (SQL Server y ORACLE). Pedro es MCP y MAP 2012, es fundador, diseñador y programador de www.devjoker.com..
Fecha de alta:08/03/2011
Última actualizacion:19/10/2011
Visitas totales:3239
Valorar el contenido:
Últimas consultas realizadas en los foros
Últimas preguntas sin contestar en los foros de devjoker.com