PDA

Visualizza versione completa : [C]NamedPipes


unomichisiada
25-08-2004, 02.02.15
Salve a todos.Supponiamo di avere due applicazioni,una che fa da client e una da server in una comunicazione interprocesso mediante NamedPipes.Supponiamo che il nome della pipe sia "\\\\.\\pipe\\mypipe" per tutte le istanze sia dei client che dei server.Nelle mie intenzioni il server deve gestire le richieste di più clients e per far questo crea più thread ognuno dei quali riceve un proprio handle alla pipe (creato grazie a CreateNamedPipe con PIPE_UNLIMITED_INSTANCES come numero massimo di istanze).
Se avvio un solo server e molti client nessun problema,il server è in grado di ricevere i dati da tutti i client,se però contemporaneamente avvio un'altro server,questo non è recettivo,cioè i dati inviati dai client vengono ricevuti solo dal primo server e da lui no,mentre io vorrei che i dati inviati da qualiasi client arrivassero a tutti i server attualmente attivi,non solo ad uno.Un pò di codice così si capisce meglio:

server:



main()
{
DWORD dwThreadID;
BOOL bConnected;
HANDLE hPipe,hThread;
while(1)
{
hPipe = CreateNamedPipe
(PIPENAME,PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE |
PIPE_WAIT,PIPE_UNLIMITED_INSTANCES,
0, 0, PIPETIMEOUT, NULL);
if(hPipe == INVALID_HANDLE_VALUE)
return -1;
bConnected = ConnectNamedPipe(hPipe, NULL) ? TRUE
: (GetLastError() == ERROR_PIPE_CONNECTED);
if(bConnected)
{
hThread = CreateThread(NULL,0,ListenerProc,hPipe,0
,&dwThreadID);
if(!hThread)
return -1;
else
CloseHandle(hThread);
}
else
CloseHandle(hPipe);
}
return 1;
}
DWORD WINAPI ListenerProc(LPVOID hPipe)
{
DWORD dwBytesRead = 0;
char buff[512];
HANDLE hThisPipe = (HANDLE)hPipe;

while (ReadFile(hThisPipe, buff, 512, &dwBytesRead, NULL))
{
buff[dwBytesRead] = 0;
//processa i dati letti
//.......
}
FlushFileBuffers(hThisPipe);
DisconnectNamedPipe(hThisPipe);
CloseHandle(hThisPipe);
return 1;
}


(Ogni client ha un solo handle alla pipe)
client:


BOOL Connect()
{
m_hPipe = CreateFile(PIPENAME, GENERIC_WRITE , 0, NULL,
OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,

NULL);
if(m_hPipe != INVALID_HANDLE_VALUE)
return TRUE;

return FALSE;
}
BOOL SendData(LPCSTR szData)
{
//.......
DWORD dwBytesWritten;
if(WriteFile(m_hPipe, szData, (DWORD)strlen(szData),
&dwBytesWritten, NULL) != FALSE &&
dwBytesWritten == (DWORD)strlen(szData))
return TRUE;
CloseHandle(m_hPipe);
//.............
return FALSE;
}



Nel server quando avvio due istanze del server CreateNamedPipe ritorna lo stesso
valore numerico di hPipe per entrambe però solo la prima istanza salta la
successiva ConnectNamedPipe(hPipe, NULL) quando arriva una connessione da un client
,l'altra si blocca li (e credo sia perchè l'handle è lo stesso ed è già impegnato)
ma allora perchè GetLastError() non mi da nessun errore.
Insomma voglio ottenere o di poter inviare da un client a tutti i server attivi oppure
di avvisare l'utente che non è possibile aprire due server con la stessa pipe,cosa che non
riesco a fare.

Il tutto sotto windows (xp)

P8257 WebMaster
25-08-2004, 09.05.18
Il "problema" è che l'API "CreateNamedPipe" può essere usata sia per creare una nuova istanza della pipe con gli attributi specificati (come nel tuo caso) e sia per reinstanziare una pipe già esistente, con gli stessi attributi o con nuovi attributi...

Nel tuo caso, lanciando più volte il server, non fai che reinstanziare la pipe creata dal primo server che lanci e che va a buon fine, tale pipe però è occupata già dal primo processo ed avendo lo stesso handle (dato che l'istanza della pipe è sempre una) ma accesso esclusiveo, tu non riesci ad usarla e tutti i processi server successivi rimangono inesorabilmente bloccati quando tentano di fare operazioni sulla pipe... GetLastError non restituisce errori.

La soluzione più semplice che mi viene in mente (tenendo conto che è mattina :D) è quella di modificare il server in modo che la sua prima istruzione sia quella di connettersi alla pipe...

Connettendoti alla pipe riceverai un errore se la pipe non esiste (quindi sarai sicuro che non esistono altri processi server) e potrai così crearla, oppure ti connetterai con successo alla pipe creata o reinstanziata da un altro o più processi server ed in questo caso fermerai l'esecuzione avvisando l'utente che un processo server è già stato lanciato...

Bye :cool:

unomichisiada
25-08-2004, 18.16.13
Ok ho capito ci provo,grazie.

unomichisiada
25-08-2004, 19.59.08
La soluzione più semplice che mi viene in mente (tenendo conto che è mattina ) è quella di modificare il server in modo che la sua prima istruzione sia quella di connettersi alla pipe...

La tua soluzione è buona in teoria ma in pratica non va bene,infatti come faccio a connettermi alla pipe per testare se è possibile connettermi senza bloccarmi in attesa?Con ConnectNamedPipe non va bene perchè essendo la pipe creata in modo WAIT ConnectNamedPipe si blocca in attesa di connessioni dall'altro lato.Spero che il problema sia chiaro.Voglio trovare una funzione che mi consenta di testare se è già attivo un'altro server (che quindi occupa l'HANDLE) senza però bloccarsi in questa fase di test.
Una cosa che non mi è chiara è perchè ai thread figli di uno stesso server CreateNamedPipe assegna Handle diversi alla pipe,mentre se avvio un'altro server l'handle ritornato è lo stesso?In pratica più server ricevono la stessa istanza della pipe mentre in teoria non dovrebbe essere così.Se la funzione non si comportasse così di fatto non avrei problemi,cioè se si comportasse con i processi pari allo stesso modo in cui si comporta con i thread di uno stesso server.

unomichisiada
25-08-2004, 21.15.50
Come non detto,ho risolto il problema con una createfile prima della createnamedpipe:

hPipe = CreateFile(PIPENAME, GENERIC_WRITE , 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
NULL);
if(hPipe == INVALID_HANDLE_VALUE)
hPipe = CreateNamedPipe(PIPENAME,PIPE_ACCESS_INBOUND,
PIPE_TYPE_BYTE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
0, 0, PIPETIMEOUT, NULL);
else
{
CloseHandle(hPipe);
return -1;
}
if(hPipe == INVALID_HANDLE_VALUE)
return -1;

Ora però ho un'altro problema.Ho bisogno di ricavare il valore di ritorno della funzione del thread (cioè il valore che metto io dopo il return nella funzione,non il codice di uscita del processo)
Ora nella documentazione msdn ho letto che bisogna usare GetExitCodeThread() e infatti l'ho usata in questo modo:

if (GetExitCodeThread(m_hThread,&dwExitCode))
{
if (dwExitCode != STILL_ACTIVE && dwExitCode < 0 )
return FALSE;
}
return TRUE;

Nella pratica però non funziona,il valore che viene memorizzato in dwExitCode è sempre lo stesso (259),qualunque sia il valore con cui è uscita la funzione i callback del thread.Ne ho assoluto bisogno è non riseco in nessun modo a ricavare questo valore di ritorno.Non è che hai qualche idea?

unomichisiada
27-08-2004, 17.10.44
Ho scoperto che 259 in realtà è il valore numerico di STILL_ACTIVE,questo significa che quando io vado a richiedere l'exit code del processo questo non è ancora terminato e invece dovrebbe dato che la funzione di callback è uscita quasi immediatamente con return -1.Anche mettendo quest'ultima istruzione come l'unica della callback (come prova) non cambiano le cose,il processo risulta comunque attivo.