Mono e Remoting: come scambiare informazioni tra Windows e Linux

di Andrea Zani, in Mono,

In un blog precedente avevo spiegato come creare un servizio con C# e Mono per macchine Linux. Oggi voglio complicare un po' le cose, perché l'obbiettivo a cui voglio giungere è lo scambio di informazioni tra i due mondi.

La prima soluzione che potrebbe venire in mente è l'utilizzo dei web services. Subito però nasce un problema di base: per poterli utilizzare dobbiamo avere un web server che gira sulla macchina, e questo non è sempre una buona soluzione perché necessità di installazioni e sistemi operativi che consentono questa operazione. Nel mondo Windows è necessario avere almeno Windows Xp Professional oppure Vista. Con qualsiasi distribuzione Linux dobbiamo installare Apache e il modulo apposito perché possano essere elaborate le pagine asp.net. Ne vale la pena? No, perché il mondo .Net permette un mezzo più potente per lo scambio di dati tra ambienti in cui gira il Framework: il Remoting.

Quei quattro gatti che mi conoscono, sanno la mia passione per il Remoting fin dalla versione 1.1 del Framework. Questa tecnologia permette lo scambio di oggetti tra applicativi scritti in .Net senza la necessità di web server, inoltre possiamo utilizzare la serializzazione binaria per la trasmissione di dati molto più prestazionale che la tradizionale SOAP via HTTP (anche se con il Remoting possiamo utilizzare sia la serializzazione SOAP, sia il trasferimento con il protocollo HTTP). Dal Framework 3.0 è presente la tecnologia WCF, molto più potente, ma il mondo Mono è ancora indietro e l'implementazione di WCF e delle novità presenti dal Framework 3.0 (nome in codice Olive sotto Mono) sono ancora abbastanza indietro.

Un articolo che confrontava i due mondi - Remoting vs web services - lo potete trovare qui. Per spiegare meglio la soluzione proposta in questo blog, ho scritto un banale progetto con MonoDevelop che è possibile scaricare dal link a fondo pagina. Questo piccolo progetto ripropone quanto già detto nel blog riguardante la creazione di servizi con Mono. Per questa occasione ho aggiunto al servizio la possibilità di richiamare una funzione all'interno di una classe direttamente grazie al Remoting.

Innanzitutto, il file allegato presenta tre progetti:

  1. Servizio che attende le chiamate via Remoting.
  2. Client che richiama la funzione via Remoting al servizio.
  3. Classe con l'interfaccia per lo scambio di informazioni.

Il progetto con l'interfaccia è una banale classe con questo codice:

using System;
public interface IRequestInformation
{
string GetNameAndDateTime(string name);
}

Molto banale: è una funzione che accetta come parametro una stringa e ritorna un'altra stringa. Ora nel servizio dobbiamo esporre questa interfaccia con una classe all'interno del servizio. Nulla di complesso:

private void _MainLoop ()
{
TcpChannel channel = new TcpChannel(1981);
ChannelServices.RegisterChannel(channel,false);
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(Information),
"RemotingMessage",WellKnownObjectMode.SingleCall);
MainClass.WriteLog(ServiceName, "Remoting started...");
while (!stopThread)
Thread.Sleep (2000);
}

_MainLoop è la funzione che è possibile trovare spiegata nel blog precedente. In quell'esempio era presente solo il ciclo while, ora è stato aggiunto il codice per l'attesa di chiamate dalla porta Tcp 1981 della classe "Information":

namespace service1
{
public class Information:MarshalByRefObject, IRequestInformation
{
public Information()
{
}
public string GetNameAndDateTime (string name)
{
MainClass.WriteLog("Remoting", "received: "+name); // scrive log
return string.Format("{0}: {1}",name, DateTime.Now);
}
}
}

Questa classe espone l'interfaccia IRequestInformation e il metodo GetNameAndDateTime. Siccome mi manca la fantasia non faccio altro che tornare una stringa contenente il name passato insieme alla data e ora attuale.

Il progetto client allegato è una console application che richiama via remoting la funzione presente nel servizio:

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace client
{
class MainClass
{
public static void Main(string[] args)
{
try
{
string name="io"; // Stringa di default se non vengono passati parametri
if (args.Length>0) name=args[0];
IRequestInformation remot;
string location = "tcp://192.168.0.7:1981/RemotingMessage";
remot = (IRequestInformation)Activator.GetObject(
typeof(IRequestInformation), location);
Console.WriteLine(remot.GetNameAndDateTime(name));
}
catch (RemotingException)
{
Console.WriteLine("Impossibile comunicare con il server");
}
}
}
}

Anche qui viene usato l'interfaccia per la creazione automatica per l'utilizzo della classe presente nel servizio. Il Framework penserà al posto nostro alla creazione di una classe proxy per la serializzazione binaria e la trasmissione.

Se si è seguito il blog precedente e il servizio gira sulla macchina (sia che si tratti di Windows che Linux), possiamo richiamare il servizio direttamente dall'eseguibile client:

client su macchina windows

Questo anche da altre macchine Linux con Mono:

client su macchina linux

In entrambi i casi il servizio ha risposta ritornando correttamente la stringa passata con la data e l'ora della richiesta. Inoltre, il nostro servizio è in grado di loggare le richieste. Vediamo che cosa ha memorizzato:

log del servizio

Il codice presente nell'allegato ha l'ip e la porta memorizzati direttamente nel codice, per modificarli è necessario ricompilare i progetti. Potevo configurare il Remoting con il file di configurazione del progetto, lo so, ma non ne avevo voglia. Ma se volete provarlo o avete questa strada o fate in modo che il servizio sia memorizzato da una macchina con ip 192.168.0.7.

Una piccola divagazione a margine: come detto nel blog precedente i test sono stati fatti su una macchina con Ubuntu. A parte la versione vecchia di Mono presente nei repository di questa distribuzione, ho scoperto un'altra anomalia. Con l'esempio presente che comunica con porte TCP, mi aspettavo di dover configurare il firewall, proprio come Windows richiede. Con Ubuntu non ho avuto alcun problema. I pacchetti sono passati senza problemi. Dopo una piccola ricerca scopro che Ubuntu ha sì il firewall attivo, ma permette il passaggio indiscrimanto da qualsiasi porta! La più famosa, e forse utilizzata, distribuzione Linux ha un firewall pressocché inutilizzato! La cosa mi ha sorpreso dato che, per esempio, OpenSuse ha il suo bel firewall attivo... Roba da Windows 2000... per fortuna almeno non ha gli stessi problemi di sicurezza.

Dimenticavo: l'allegato è possibile scaricarlo da questo link. La piadina che ho mangiato a cena ce l'ho ancora sullo stomaco. Questa è un'altra divagazione.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese