Newsgroup: it.comp.lang.c++
Titolo: Comunicazione tra applicazioni

Ciao, utilizzo da poco il C++ Builder e mi sono scontrato con questo
problema:
voglio realizzare tre applicazioni (chiamiamole A, B e C) che si comportino
cosi':
eseguo A che istanzia e valorizza l' oggetto O1; eseguo B contenente un
bottone alla cui pressione viene lanciata l'applicazione C che in qualche
maniera (non so proprio quale!) accede all'oggetto O1 e se ne fa una copia
propria.
Ringrazio anticipatamente chiunque possa aiutarmi.

propria.
Anzitutto non mi e' chiaro cosa intendi per copia di un oggetto.
Intendi forse dire che l'applicazione C possiede la conoscenza dei metodi
dell'oggetto O1, ad esempio perche' possiede a livello di codice
il compilato di almeno una interfaccia dell'oggetto 01 (cioe' conosce un
antenato
-> una classe base astratta di O1)?
Esempio:
class Interface {
public:
    int DoSomething()=0;
}
class CBase:public Interface {
    int DoSomething(){return 1;}    ;
}
Nell'applicazione A
CBase MyObj ; // istanziazione
Nell'applicazione C
Interface *pMyInterface;
GetObjectFromA(&pMyInterface);
pMyInterface->DoSomething();
Oppure C possiede la conoscenza
di tutta l'implementazione di un qualsiasi oggetto di cui O1 e' una istanza
ed ha solo bisogno di conoscere gli "instance data" dell'oggetto 01?
Esempio (con le definizioni di classe di cui sopra)
Applicazione A:
CBase MyObj ; // istanziazione
Applicazione C
CBase *pMyConcreteObj;
GetObjectFromA(&pMyConcreteObj);
pMyConcreteObj->DoSomething();
Per rimanere generali:
Inquadrando il problema come quello del passaggio di un blocco di memoria
tra due spazi di indirizzamento, Win32 mette a disposizione, ad esempio:
- messaggio WM_COPYDATA
- Shared section in DLL mappate da due processi
- Memory Mapped File
- Named pipes
Se hai volgia e tempo di studiare qualcosa di utile, allora COM mette a
disposizione
funzioni e interfacce per fare quello che stai cercando, occupandosi perfino
del passaggio dei parametri tra metodi invocati in diversi spazi di
indirizzamento,
perfino tra cumputer diversi (IMarshal...).



 - Named pipes
Davvero si possono usare le named pipe sotto win32?
Non e' una funzionalita' tipica dei file system Unix-like? (Le ho usate
sotto Linux...)
Dove trovo informazioni?
Non ho trovato niente ne' sul Petzold ne' sul Richter...
-- 
---------------------------------
Fabio Luchetta -  Pesaro  - Italy
Remove NOSPAM to reply via e-mail
---------------------------------

 sul Richter non ho trovato niente
Bhe', allora prova a cercare
sul newsgroup it.comp.lang.c++, in attesa che
tutti votino it.comp.programmare.win32.
Se non lo hai ancora fatto affrettati!
/****************************/
/*  SERVER                  */
/****************************/
#include <windows.h>
#include <stdio.h>
#define SIZE  (512)
#define PIPE_TIMEOUT (50000)
int main(int argc, char ** argv){
 HANDLE hPipe;
    char   pBuff[SIZE];
 DWORD  nRead,nWritten;
 strcpy(pBuff,"Hello, what is yout name ?");
 hPipe = CreateNamedPipe("\\\\.\\pipe\\MyPipe",
                      PIPE_ACCESS_DUPLEX,
       PIPE_TYPE_BYTE |
                         PIPE_WAIT,                // blocking mode
                            PIPE_UNLIMITED_INSTANCES, // max. instances
                            SIZE,                  // output buffer size
                            SIZE,                  // input buffer size
                            PIPE_TIMEOUT,             // client time-out
                            NULL);                    // no security
attribute
    if (hPipe!=INVALID_HANDLE_VALUE){
  // wait for a client to connect
  if (ConnectNamedPipe(hPipe,NULL)){
   WriteFile(hPipe,pBuff,SIZE,&nWritten,NULL);
   // ensures client has read all data
   FlushFileBuffers(hPipe);
   //SetNamedPipeHandleState();
   ReadFile(hPipe,pBuff,SIZE,&nRead,NULL);
   DisconnectNamedPipe(hPipe);
   CloseHandle(hPipe);
   printf("CONVERSATION: %s \r\n",pBuff);
  } else {
   printf("NO ConnectNamedPipe %d\r\n",GetLastError());
  };
 } else {
  printf("NO CreateNamedPipe %d\r\n",GetLastError());
 };
 return 1;
}
/****************************/
/*  CLIENT                  */
/****************************/
#include <windows.h>
#include <stdio.h>
#define SIZE  (512)
int main(int argc, char ** argv){
 HANDLE hPipe;
 char pBuff[SIZE];
 DWORD nLen,nWritten,nRead;
 // WaitNamedPipe if the server is unavailable
 hPipe = CreateFile("\\\\.\\pipe\\MyPipe",
                 GENERIC_READ|GENERIC_WRITE,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL);
 if (hPipe!=INVALID_HANDLE_VALUE){
  ReadFile(hPipe,pBuff,SIZE,&nRead,NULL);
  //SetNamedPipeHandleState();
  strcat(pBuff,"I am STEPHY\r\n");
  nLen=strlen(pBuff);
  WriteFile(hPipe,pBuff,nLen,&nWritten,NULL);
  FlushFileBuffers(hPipe);
  CloseHandle(hPipe);
 } else {
  printf("NO CreateFile %d\r\n",GetLastError());
 };
 return 1;
}





Davvero si possono usare le named pipe sotto win32?
Sicuramente si` sotto NT Server; credo di no sotto 95; non
so sotto 98 e NT Workstation (questo, per il lato server; il
lato client dovrebbe essere usabile da tutti i sistemi Win32).
C'e` un esempio sull'SDK di Windows chiamato Npserver
e Npclient che mostra come fare; le API rilevanti sono
le varie ConnectNamedPipe, DisconnectNamedPipe,
CreateNamedPipe, eccetera.
Alex

 Se non lo hai ancora fatto affrettati!
Appena fatto anche se esistendo it.comp.prog avrei preferito
it.comp.prog.win32
Grazie per la risposta e per il codice di esempio.
Fabio

 CreateNamedPipe, eccetera.
Grazie per le informazioni. Dalla documentazione dell'SDK sembra che la
named pipe puo' essere creata solo sotto NT (secondo me non e' corretto
parlare di lato server di una pipe) ma acceduta anche da 95.
Mi sembra strano una distinzione tra NT Server e NT Workstation, davvero
ci sono differenze a livello di API tra le due versioni di NT?
Fabio

ci sono differenze a livello di API tra le due versioni di NT?
In teoria possono essercene, in pratica, storicamente, si e`
trattato almeno sinora di differenze "transitorie".
Alex

 sempre, coerentemente, il termine "FIFO".
Invece chiamarle FIFO o named pipe non cambia molto secondo me.
Perche' il concetto sarebbe molto diverso da una pipe?
Si tratta sempre di un canale di comunicazione tra processi, correlati
tra di loro in un caso, qualsiasi nell'altro.
Ecco un paragrafo del SDK che chiarisce qualcosa:
"Any process can act as both a server and a client, making peer-to-peer
communication possible. As used here, server refers to a process that
uses CreateNamedPipe to create a named pipe, and client refers to a
process that uses CreateFile or CallNamedPipe to connect to an instance
of a named pipe. "
Si tratta quindi di comunicazione peer-to-peer, ma viene usato il
termine "server" in maniera un po' artificiosa per indicare il processo
che crea la named pipe.
Fabio

che crea la named pipe.
Continuo a non vedere cosa vi sia di "artificioso" in questa
nomenclatura.  Il servente della pipe e` chi la crea, il cliente
della pipe, chi usa la pipe creata da altri.  Naturalmente, dato
un qualunque meccanismo M client/server, puoi ("artificiosamente")
simulare una comunicazione peer-to-peer usando una coppia
(M1, M2) di questi meccanismi: il processo A e` servente di
M1 e cliente di M2, il processo B, al contrario, cliente di M1 e
servente di M2; questo non toglie la asimmetria del meccanismo
M, ma una coppia antisimmetrica di canali ciascuno asimmetrico
acquisisce una "supersimmetria d'insieme" -- non e` ovvio?
Consideriamo per analogia il principale meccanismo di comunicazione
usato oggi a basso livello fra processi su macchine distinte: le
socket.  Anche qui, diciamo servente della socket il processo che
la crea e aspetta che qualcuno vi si connetta, cliente della socket,
il processo che si connette alla socket creata da un altro; il ruolo
di "nome", per la socket, e` svolto dal numero di porta IP.  Anche
qui, puoi certamente usare le socket per la comunicazione peer
to peer -- due diversi processi possono certamente essere
server/client, e client/server, su distinte socket.  L'analogia e`
insomma assai stretta.  E allora che vuoi fare, chiamar dunque
"artificiosa" anche la nomenclatura client/server relativa ai
processi che usano socket?!
Le FIFO (cosiddette named pipe) di vari dialetti Unix differiscono
abbastanza dalle c.d. named pipe di Win32; ad esempio, e`
abbastanza tipico di molti dialetti Unix che una FIFO _non_
possa essere usata via rete, ma solo su di un singolo nodo,
mentre in Win32 le named pipe supportano sempre la
comunicazione fra nodi diversi di una rete; in vari dialetti Unix,
una FIFO puo` "esistere" anche se in un dato momento nessuno
la sta "servendo", mentre, in Win32, una named pipe, come una
socket, e` "transiente" ("esiste" se e solo se viene "servita"); e
cosi` via.  La piena conoscenza di tutte queste varie distinzioni
(fra svariati dialetti di Unix, svariati dialetti di Win32, e fra gli
uni e gli altri) e` necessaria per decidere quali, dell'enorme
varieta` di meccanismi di comunicazione fra processi a basso
livello offerti da ciascun sistema, e ciascun dialetto di ciascun
sistema, scegliere per ogni particolare esigenza.
Personalmente, ho sempre trovato che, se devo usare un
meccanismo di basso livello, che supporti la rete, e magari
l'interoperabilita` fra diversi sistemi Unix e/o Win32, le socket
sono di solito la scelta migliore (se possibile usare protocolli
di livello superiore, come le Remote Procedure Call e cosi`
via, naturalmente, tanto meglio!); se l'esigenza e` ristretta ad
un singolo nodo della rete, le possibilita` si moltiplicano (anche
qui, quando applicabili, i protocolli di livello superiore, come ad
esempio quelli del COM, Component Object Model, su Win32,
o di ILU su Unix, offrono sostanziali vantaggi, naturalmente).
Non mi e` mai capitato di decidere che le "named pipe" di Win32
(e, putacaso, neppure il "DDE" nello stesso ambiente) fossero
la miglior soluzione per un dato problema, ma forse e` solo che
non mi sono mai capitati i problemi "ideali" per questi meccanismi;
similmente, non mi e` mai capitato di scegliere come meccanismi
su Unix le "message queues" tipiche di Unix System V, immagino
per ragioni analoghe.  In entrambi i casi, la perdita di portabilita`
fra dialetti del relativo ambiente (niente serventi named pipe su
Win95, niente "message queues" su Unix BSD 4.3, ecc ecc) ha
giocato il suo ruolo nell'orientarmi a favore di scelte diverse.
Alex

...
Il resto tutto ok, complimenti per la ricca esposizione.
Fabio

 solo il processo A sarebbe server il che non e' vero.
Win32 non lo conosco, ma sotto Unix la maniera standard e' questa: il
processo primario crea una pipe, ottenendo due descrittori per 
input ed output; poi, attraverso fork, genera un secondario che
eredita la pipe; infine uno dei due processi chiude l'estremita' in
lettura, l'altro quella in lettura, e rimane una IPC unidirezionale.
Per una comunicazione bidirezionale servono due pipes, una delle quali
viene gestita per IPC primario -> secondario e l'altra per IPC
secondario -> primario.
-- 
Maurizio Loreti                      
http://wwwcdf.pd.infn.it/~loreti/mlo.html
Un. of Padova, Dept. of Physics - Padova, Italy          
lor...@padova.infn.it

solo il processo A sarebbe server il che non e' vero.
Ma si che e` vero!  E non e` questione di nomenclatura "win32": stai
continuando a ignorare i miei riferimenti al testo di Stevens, che e`
relativo esclusivamente a Unix (vari dialetti) e fu scritto ben prima
che "win32" esistesse.
Di nuovo, considera l'analogia con le socket, per le quali pare tu
non voglia considerare "artificiosa" la nomenclatura di "server"
e "client".  Se i processi A e B si parlano su due socket; ma per
entrambe e` A a crearle, e B si limita a connettervisi; allora non
e` forse A il processo server di entrambe le socket?!
La comunicazione (sia su socket, fifo, pipe, piu` di uno di questi
schemi, ecc ecc) puo` essere bidirezionale comunque, e questo
non inficia la (potenziale) natura "server" o "client" dell'uno, o
dell'altro, processo, o di entrambi.  "Client" e` un processo che
inizia una "transazione", mandando o chiedendo informazioni
all'altro processo, "server", che normalmente "sta li` ad aspettare"
(esistono naturalmente vari schemi in cui un processo server non
"sta sempre li`" bensi` viene fatto "partire just-in-time quando
cio` viene richiesto", ad esempio da un "meta-server" di qualche
tipo; comunque il "client" resta quello che "prende l'iniziativa di
iniziare la comunicazione").  Che mai c'e` di artificioso in questa
terminologia?!  Ora, per le cosiddette "named pipe" win32, come
per le socket, ecc, il processo che "prende l'iniziativa di iniziare
la comunicazione" e` quello che fida sulla "pre-esistenza" del
canale di comunicazione (named-pipe o socket che sia), canale
sul quale presume di trovare una controparte in attesa (anche se
in realta` la controparte, il server, puo` in certi casi venir fatto
partire "al volo" dopo che il cliente ha iniziato la sua richiesta);
cioe`, il processo client e` quello che NON crea il canale di
comunicazione (bensi` vi si connette) -- il processo server e`
l'altro.  Le socket, e le named-pipe win32, sono asimmetriche da
questo punto di vista, per cui la distinzione cliente/servente non
e` affatto "terminologia arbitraria", bensi`, in entrambi i casi, e`
perfettamente rispondente.  Altri meccanismi di comunicazione
non presentano queste asimmetrie; ad esempio, la "shared
memory" puo` essere "creata" *simmetricamente* da piu`
processi con le stesse system call -- non vi e` necessariamente
una distinzione fra "creare" e "connettere" -- cio` non impedisce
di USARLA "alla client-server", ma una tale "disciplina d'uso" va
superimposta "esternamente", non e` intrinseca al meccanismo
in quanto tale, bensi` (se esiste) e` affidata a "convenzione".
Sono certamente concepibili situazioni peculiari in cui si
sceglie di scindere la distinzione "chi inizia una transazione"/
"la controparte che sta in attesa per ``servire'' la transazione
quando serve", da quella "chi si connette a un canale di
comunicazione esistente"/"chi quel canale lo rende disponibile
e vi si pone in attesa".  Ad esempio, potrei avere un processo
"meta-gestore" che crea tutte le infrastrutture di comunicazione,
genera una collezione di processi-figli, poi passa a detti figli
diverse handle di comunicazione (gia` "aperte"/"create"/
"connesse") e indicazioni sul loro ruolo (cliente per certe
cose, servente per certe altre, ecc ecc).  In questo senso,
"NULLA" e` (_solo_) "client/server".  Ma questo e` come
dire che, in complesse strutture societarie, il ruolo di "debito"
e "credito" puo` essere difficile da discernere; _normalmente_,
la distinzione fra "debitore" e "creditore" e` utile, chiara,
e TUTT'ALTRO che artificiosa -- anche nei casi in cui due
aziende A e B "si devono soldi a vicenda" in entrambi i sensi,
questo di norma si applica a _molteplici diverse transazioni_
e in ogni singola transazione i due "versi" si distinguono.
Alex

secondario -> primario.
Notiamo, sempre per completezza, che alcuni dialetti di
Unix permettono anche di usare una singola pipe in modo
bidirezionale; queste vengono a volte chiamate "stream
pipe" e, se non sbaglio, furono a suo tempo una innovazione
di Unix System V Release 4.  Stevens, Advanced Unix
Programming, ha un'ottima trattazione di tutti questi temi,
e puo` essere caldamente raccomandato a chiunque sia
interessato ad approfondirli; per Win32, oltre a Stevens
(che resta comunque MOLTO interessante da studiare),
posso raccomandare Richter, Advanced Win32 Programming.
L'aggettivo "Advanced" in ENTRAMBI questi titoli e` del
tutto giustificato; ne` lo Stevens, ne` il Richter possono
essere veramente raccomandati a chi non abbia GIA` una
buona competenza, sia di programmazione in generale,
sia sugli specifici ambienti che i testi approfondiscono.  A
parte questo "dettaglio", si tratta comunque di OTTIMI
scrittori tecnici, con grande padronanza della propria
materia e notevoli capacita` di spiegazione e chiarezza.
Alex

 in quanto tale, bensi` (se esiste) e` affidata a "convenzione".
Per l'appunto anche le FIFO mi sembrano appartenere a questi altri
meccanismi di comunicazione senza asimmetrie. Se sotto Unix creo una
FIFO con mkfifo dalla shell, poi lancio due processi uno che fa da
server e uno da client, nessuno dei due ha creato la FIFO ma la usano
uno come server e uno come client. Invece sotto win32 entrambi i
processi sarebbero client e il server sarebbe un analogo del mkfifo, e
magari non puo' terminare (sotto win32 se il processo che crea la FIFO
termina, la FIFO esiste ancora?).
Fabio

termina, la FIFO esiste ancora?).
Si`, purche`, e sino a quando, qualcuno vi sia connesso.  La
regola e`, per citare l'help in linea di Win32:
"""
An instance of a named pipe is always deleted when the 
last handle to the instance of the named pipe is closed
"""
In pratica, e` la stessa regola che, sia su Win32 sia su
Unix, si applica alle socket, alle aree di shared memory,
e alla gran parte dei canali di comunicazione; in Unix,
invece, la persistenza delle fifo e` maggiore (piu` simile
a quella dei file), cioe` "sino a quando non esplicitamente
cancellato".  Ma i _dati_ della fifo non persistono
comunque, e` solo la sua "esistenza" a farlo, quindi
la differenza non e` cosi` grande come potrebbe a
prima vista sembrare.
Alex

Alex Martelli wrote:
-> ...
-> ->Invece sotto win32 entrambi i processi sarebbero client
-> 
-> No, l'approccio client/server e` ancora applicabile, se lo si
-> desidera; infatti, uno dei due processi, ricevendo la
-> handle di namedpipe fatta dal processo "sopra" (con
-> l'API CreateNamedPipe), puo` connettervisi con la
-> ConnectNamedPipe, il che lo identifica come servente;
-> l'altro potrebbe ricevere il nome e connetterlo invece
-> con CallNamedPipe, il che lo identifica come cliente.
Ma perche' tirare fuori due funzioni che fanno la stessa cosa come la
system call open? Forse perche' una funziona solo su NT e l'altra anche
su 95? Questa e' la piu' grossa asimmetria che vedo!
-> Cioe`, la simmetria e` tutt'altro che "forzata", anche se
-> scegli di effettuare comunque la creazione in un
-> processo separato.  Naturalmente, puoi forzarla
-> scegliendo invece di passare la handle a entrambi
-> i processi (ma *attenzione*: la handle e` valida solo
-> sul nodo stesso dove e` stata generata; il nome, invece,
-> e` valido anche su altri nodi della rete -- considerazione
-> che non si applica su Unix, visto che in quell'ambiente
-> anche il nome e` soggetto alla limitazione relativa al
-> non essere usabile in rete).
L'introduzione del supporto di rete porta un'altra asimmetria che non
c'era (la FIFO risiede su un host ma non sugli altri).
-> ->e il server sarebbe un analogo del mkfifo, e
-> ->magari non puo' terminare (sotto win32 se il processo che crea la
FIFO
-> ->termina, la FIFO esiste ancora?).
-> 
-> Si`, purche`, e sino a quando, qualcuno vi sia connesso.  La
-> regola e`, per citare l'help in linea di Win32:
-> """
-> An instance of a named pipe is always deleted when the
-> last handle to the instance of the named pipe is closed
-> """
-> In pratica, e` la stessa regola che, sia su Win32 sia su
-> Unix, si applica alle socket, alle aree di shared memory,
-> e alla gran parte dei canali di comunicazione; in Unix,
-> invece, la persistenza delle fifo e` maggiore (piu` simile
-> a quella dei file), cioe` "sino a quando non esplicitamente
-> cancellato".  Ma i _dati_ della fifo non persistono
-> comunque, e` solo la sua "esistenza" a farlo, quindi
-> la differenza non e` cosi` grande come potrebbe a
-> prima vista sembrare.
Pero' il bisogno di "tenere in vita" la FIFO con processi che la usano
e' un'altra causa di asimmetria che porta alla presenza di funzioni
"antagoniste" come CreateNamedPipe/ConnectNamedPipe vs. CallNamedPipe.
La mia impressione e' quindi che su Unix le FIFO siano simmetriche di
default mentre su Win32 siano asimmetriche di default a causa delle
differenze introdotte (supporto solo parziale di win95, supporto di rete
e minore persistenza).
A questo punto il riferimento a "processi server" nella documentazione
del SDK e' coerente con questa visione asimmetrica diventata necessaria.
Fabio