PDA

View Full Version : IPC tra processo C e processo JAVA


topix93
10-01-2013, 16:02
Quello che ho bisogno di realizzare è un canale di comunicazione tra un processo scritto in c ed un processo scritto in java. Sto cercando il metodo piu performante per ottenere ciò e mi sono imbattuto in questo articolo: http://homes.cs.ru.ac.za/csgw/Research/WellsPDPTA2009.pdf che mostra al fondo un grafico "prestazionale" dei vari metodi per la comunicazione tra processi. Il piu veloce pare che sia il metodo delle Named Pipes (detto anche FIFO).
Con questo metodo volevo fare in modo che:
- Il processo C crea la named pipe;
- Il processo JAVA si mette in ascolto/lettura;
- Il processo C scrive dei dati sulla named pipe;
- il processo JAVA legge i dati;
- il processo C riscrive altri dati... ed il processo si ripete

E possibile fare una cosa di questo tipo?

Vincenzo1968
10-01-2013, 16:31
Non conosco bene Java ma se con tale linguaggio puoi utilizzare le named pipe si può fare benissimo.

Attendiamo qualcuno che conosce bene Java(ma intanto googlo un attimino ).

Edit: googlato. Sembrerebbe che si possa fare:

http://stackoverflow.com/questions/4112480/named-pipes-between-java-and-c-c-programs

You can simply start an external process in Java and connect to it's pipes.

// Execute command
String command = "ls";
Process child = Runtime.getRuntime().exec(command);

// Get pipes from process
InputStream in = child.getInputStream();
OutputStream out = child.getOutputStream();
InputStream error = child.getErrorStream();


Ariedit:
un po' più sotto, l'osservazione di un altro utente:

although i find your proposed solution quite usable, what you are describing are not named pipes. named pipes have to be addressable from the outside (in case of *nix systems through the file system).

topix93
10-01-2013, 17:02
Quello che hai trovato tu non sono named pipe ma sono bensì pipe la differenza sta nel fatto che le pipe possono essere utilizzate solo se due processi hanno un "antenato" comune...

Non ho ancora avuto l'occasione di provare l'esempio che hai messo tu ma se dovesse funzionare bene potrei anche adottare quel metodo.

il problema piu che altro è che su internet non si trovano molti esempi e quindi non so esattamente come si fa a implementare questa cosa.

cio che voglio fare è lasciare a java la gestione dell'interfaccia grafica e affidare al c i compiti piu pesanti... per fare cio pero ho bisogno di una comunicazione bidirezionale per inviare comandi e ricevere i dati

spero di essermi spiegato

pabloski
10-01-2013, 18:31
named pipes o sockets, sono queste le scelte più gettonate

VICIUS
10-01-2013, 21:40
Per quanto riguarda java non servono classi speciali. Basta che apri la pipe con un Input/OutputStream e ci puoi fare quello che vuoi. Per crearle da codice però non ti saprei aiutare. Quelle poche volte che le ho usate ho sempre fatto da terminale con mkfifo.

Vincenzo1968
10-01-2013, 22:20
Forse ho trovato qualcosa:

http://ubuntuforums.org/showthread.php?t=372505

http://v01ver-howto.blogspot.it/2010/04/howto-use-named-pipes-to-communicate.html

Il client in Java proposto nel link precedente:

Java: Echo client

try {
// Connect to the pipe
RandomAccessFile pipe = new RandomAccessFile("\\\\.\\pipe\\testpipe", "rw");
String echoText = "Hello word\n";
// write to pipe
pipe.write ( echoText.getBytes() );
// read response
String echoResponse = pipe.readLine();
System.out.println("Response: " + echoResponse );
pipe.close();

} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


http://magdalena.thedarkmere.net/~beau/code/NamedPipe.java.html

topix93
11-01-2013, 08:26
Sono riuscito a fare comunicare i due processi attraverso le named pipe... ma ora mi viene un dubbio...

Se il processo C scrive una linea e il processo JAVA la legge, e diciamo che questo processo continua all'infinito, è possibile che i due processi si disincronizzino?

Un'altra cosa, dato che questo processo dovra ciclare all'infinito ci può essere qualche problema? Dato che le named pipe sono come dei file è possibile che dopo un po raggiungano una dimensione molto alta?

VICIUS
11-01-2013, 08:53
Sono riuscito a fare comunicare i due processi attraverso le named pipe... ma ora mi viene un dubbio...

Se il processo C scrive una linea e il processo JAVA la legge, e diciamo che questo processo continua all'infinito, è possibile che i due processi si disincronizzino?

Un'altra cosa, dato che questo processo dovra ciclare all'infinito ci può essere qualche problema? Dato che le named pipe sono come dei file è possibile che dopo un po raggiungano una dimensione molto alta?

Lettura e scrittura da e su una pipe sono operazioni bloccanti. Se java prova a leggere qualcosa dalla pipe ma non c'è niente la read rimane in attesa all'infinito. Stessa cosa per le scritture. Se il programma C prova a scrivere su una pipe piena da cui nessuno legge questa rimane bloccata finché qualcuno non legge i dati che erano già stati scritti.

Per quanto riguarda la dimensione massima che possono raggiungere mi pare di ricordare qualcosa come 4k. Ma prendilo con le pinze. Ti conviene cercare nella documentazione del tuo sistema per sicurezza.

shinya
11-01-2013, 08:59
Ma usare ZeroMQ, tipo?

topix93
11-01-2013, 09:09
ho provato a fare un ciclo dove il programma in c continua a scrivere e il programma java continua a leggere... dopo un po si nota che il C ha scritto sulla pipe piu linee di quante ne abbi lette java

Questa è l'immagine del test che ho fatto: http://img842.imageshack.us/img842/2383/testxx.png

Di sopra c'è il programma in c che scrive un progressivo e di sotto il programma java che legge... si nota che si sono sfasati di circa 100 messaggi...

comunque i 4 kb si riferiscono al dimensione del testo massima prima del carattere di fine linea

Vincenzo1968
11-01-2013, 09:24
Sembra la vecchia freddura sui carabinieri che vanno sempre in coppia perché l'uno sa scrivere e l'altro sa leggere.


:D

topix93
11-01-2013, 09:24
zeromq non l'ho avevo mai visto... ma mi interessa molto come soluzione... sapete dove posso trovare un esempio per la comunicazione tra due soli processi?

shinya
11-01-2013, 09:32
zeromq non l'ho avevo mai visto... ma mi interessa molto come soluzione... sapete dove posso trovare un esempio per la comunicazione tra due soli processi?

http://zguide.zeromq.org/page:all
All'inizio c'è un esempio di request-reply pattern. Sostanzialmente è come aprire un socket, ma con gli steroidi. Ci sono esempi in vari linguaggi.

Vincenzo1968
11-01-2013, 13:24
http://img18.imageshack.us/img18/8770/javapipe.jpg

Ho provato a compilare l'esempio del link ma quando tento di eseguire il file ClientPipeJava.class ho il seguente errore:



Comando:

java ClientPipeJava



Exception in thread "main" java.lang.NoClassDefFoundError: ClientPipeJava (wrong
name: clientpipejava/ClientPipeJava)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)



/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package clientpipejava;

import java.io.*;
import java.util.*;

/**
*
* @author VLC
*/
public class ClientPipeJava
{

/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
// TODO code application logic here
try
{
// Connect to the pipe
RandomAccessFile pipe = new RandomAccessFile("\\\\.\\pipe\\mynamedpipe", "rw");
String echoText = "Hello word\n";
// write to pipe
pipe.write ( echoText.getBytes() );
// read response
String echoResponse = pipe.readLine();
System.out.println("Response: " + echoResponse );
pipe.close();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}




java -version
java version "1.7.0_09"
Java(TM) SE Runtime Environment (build 1.7.0_09-b05)
Java HotSpot(TM) Client VM (build 23.5-b02, mixed mode, sharing)


NetBeans IDE 7.2.1

Perché? :confused:

VICIUS
11-01-2013, 13:36
Te sei scordato il package. Non ho provato ma se lo lanci così va di sicuro.
java clientpipejava.ClientPipeJava

Vincenzo1968
11-01-2013, 13:43
http://img827.imageshack.us/img827/930/javapipe2.jpg

:muro: :mad: :nera: :cry:

shinya
11-01-2013, 13:45
http://img827.imageshack.us/img827/930/javapipe2.jpg

:muro: :mad: :nera: :cry:
Eh dai, fossero questi i problemi veri...

VICIUS
11-01-2013, 13:51
http://img827.imageshack.us/img827/930/javapipe2.jpg

:muro: :mad: :nera: :cry:
Ti sarai scordato di creare la cartella del package. :asd:

Vincenzo1968
11-01-2013, 13:55
Che cartella? Dove la debbo creare? Non ci pensa NetBeans automaticamente?

Comunque ho tolto il package


//package clientpipejava;



e funziona quasi tutto. cioè il programma si avvia senza errori ma il server mi stampa "?????" al posto della stringa "Hello word".

Il server, in C, funziona correttamente con un client in C.

:confused:

topix93
11-01-2013, 14:46
Guarda ti metto i miei 2 codici C e JAVA che ho utilizzato:

C:
#include <stdio.h>
#include <conio.h>
#include <windows.h>

int main(int argc, const char **argv)
{
printf("Creating an instance of a named pipe...");
// Create a pipe to send data
HANDLE pipe = CreateNamedPipe(
"\\\\.\\pipe\\temp", // name of the pipe
PIPE_ACCESS_DUPLEX | WRITE_DAC, // 1-way pipe -- send only
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, // send data as a byte stream
1, // only allow 1 instance of this pipe
4096, // OutBufferSize
4096, // InBuffersize
2000, // TimeOut
NULL // use default security attributes
);

if (pipe == NULL || pipe == INVALID_HANDLE_VALUE) {
printf("Failed to create outbound pipe instance.");
// look up error code here using GetLastError()
getch();
return -1;
}

printf("Waiting for a client to connect to the pipe...");

// This call blocks until a client process connects to the pipe
bool result = ConnectNamedPipe(pipe, NULL);
if (!result) {
printf("Failed to make connection on named pipe.");
// look up error code here using GetLastError()
CloseHandle(pipe); // close the pipe
system("pause");
return 1;
}
printf("Sending data to pipe...\n");

// This call blocks until a client process reads all the data
char data[1024];
DWORD numBytesWritten = 0;

sprintf(data, "This is test line 1 so there.\n");
result = WriteFile(
pipe, // handle to our outbound pipe
data, // data to send
lstrlen(data), // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
);

if (result) {
printf("Number of bytes sent: %d\n\n", numBytesWritten);
} else {
printf("Failed to send data.\n\n");
// look up error code here using GetLastError()
}
// look up error code here using GetLastError()

// Close the pipe (automatically disconnects client too)
CloseHandle(pipe);

printf("Done.\n\n");
return 0;
}

JAVA
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

public class Reader {

/**
* @param args
*/
public static void main(String[] args) {
// Connect to the pipe
RandomAccessFile pipe;
try {
pipe = new RandomAccessFile("\\\\.\\pipe\\temp", "r");

String echoResponse = null; //= pipe.readLine();

echoResponse = pipe.readLine();

System.out.println("Response: " + echoResponse );

pipe.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}

}

Vincenzo1968
11-01-2013, 15:00
Grazie mille, topix, gentilissimo!

Ho visto che apri la pipe con "r" mentre io la apro con "rw". Potrebbe dipendere da questo? Faccio la prova...

pipe = new RandomAccessFile("\\\\.\\pipe\\temp", "r");

Vincenzo1968
11-01-2013, 15:09
No, mi dice giustamente "accesso negato" se tolgo l'attributo write.

Vincenzo1968
11-01-2013, 15:50
anche così:


//pipe.write ( echoText.getBytes("US-ASCII") );
//pipe.write ( echoText.getBytes("UTF-8") );
pipe.write ( echoText.getBytes("ASCII") );



il server mi stampa "?????"

:confused:

topix93
11-01-2013, 16:19
Non capisco... non vorrei che dipenda poi da visual studio... io ti posso assicurare che funziona ti metto il link del mio progetto, il progetto in C l'ho sviluppato con Blodsheed dev-c++

ecco il link https://www.4shared.com/rar/DjNlpwwZ/FIFO.html? forse devi essere registrato per scaricarlo, ma comunque la registrazione è gratuita

Vincenzo1968
11-01-2013, 16:46
Ci siamo!

Bisognava inviare la stringa nel formato UTF-16 Little Endian(questo almeno su Windows; su Linux non lo so, debbo provare):


pipe.write ( echoText.getBytes("UTF-16LE") );


Così funziona alla grande!

http://img811.imageshack.us/img811/4497/javapipe3.jpg

:yeah: :winner: :yeah:


Il codice Java client completo :


import java.io.*;
import java.util.*;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;


/**
*
* @author VLC
*/

public class ClientPipeJava
{

public static void main(String[] args)
{
// TODO code application logic here
try
{
// Connect to the pipe
RandomAccessFile pipe = new RandomAccessFile("\\\\.\\pipe\\mynamedpipe", "rw");
//String echoText = "Hello word\n";

String echoText = null;
echoText = "Hello word\n";
// write to pipe
//pipe.write ( echoText.getBytes() );
//pipe.write ( echoText.getBytes("US-ASCII") );
//pipe.write ( echoText.getBytes("UTF-8") );
//pipe.write ( echoText.getBytes("UTF-16") );
//pipe.write ( echoText.getBytes("UTF-16BE") );
pipe.write ( echoText.getBytes("UTF-16LE") );
//pipe.write ( echoText.getBytes("ASCII") );

// read response
String echoResponse = pipe.readLine();
System.out.println("Response: " + echoResponse );
pipe.close();
}
catch (Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}


Questo è il codice C del server(copincollato da MSDN):


// Server.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#define BUFSIZE 4096

VOID InstanceThread(LPVOID);
VOID GetAnswerToRequest(LPTSTR, LPTSTR, LPDWORD);

int _tmain(int argc, _TCHAR* argv[])
{
BOOL fConnected;
DWORD dwThreadId;
HANDLE hPipe, hThread;
LPTSTR lpszPipename = TEXT("\\\\.\\pipe\\mynamedpipe");

// The main loop creates an instance of the named pipe and
// then waits for a client to connect to it. When the client
// connects, a thread is created to handle communications
// with that client, and the loop is repeated.

for (;;)
{
hPipe = CreateNamedPipe(
lpszPipename, // pipe name
PIPE_ACCESS_DUPLEX, // read/write access
PIPE_TYPE_MESSAGE | // message type pipe
PIPE_READMODE_MESSAGE | // message-read mode
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
NMPWAIT_USE_DEFAULT_WAIT, // client time-out
NULL); // default security attribute

if (hPipe == INVALID_HANDLE_VALUE)
{
printf("CreatePipe failed");
return 0;
}

// Wait for the client to connect; if it succeeds,
// the function returns a nonzero value. If the function
// returns zero, GetLastError returns ERROR_PIPE_CONNECTED.

fConnected = ConnectNamedPipe(hPipe, NULL) ?
TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);

if (fConnected)
{
// Create a thread for this client.
hThread = CreateThread(
NULL, // no security attribute
0, // default stack size
(LPTHREAD_START_ROUTINE) InstanceThread,
(LPVOID) hPipe, // thread parameter
0, // not suspended
&dwThreadId); // returns thread ID

if (hThread == NULL)
{
printf("CreateThread failed");
return 0;
}
else CloseHandle(hThread);
}
else
// The client could not connect, so close the pipe.
CloseHandle(hPipe);
}
return 1;
}

VOID InstanceThread(LPVOID lpvParam)
{
TCHAR chRequest[BUFSIZE];
TCHAR chReply[BUFSIZE];
DWORD cbBytesRead, cbReplyBytes, cbWritten;
BOOL fSuccess;
HANDLE hPipe;

// The thread's parameter is a handle to a pipe instance.

hPipe = (HANDLE) lpvParam;

while (1)
{
// Read client requests from the pipe.
fSuccess = ReadFile(
hPipe, // handle to pipe
chRequest, // buffer to receive data
BUFSIZE*sizeof(TCHAR), // size of buffer
&cbBytesRead, // number of bytes read
NULL); // not overlapped I/O

if (! fSuccess || cbBytesRead == 0)
break;
GetAnswerToRequest(chRequest, chReply, &cbReplyBytes);

// Write the reply to the pipe.
fSuccess = WriteFile(
hPipe, // handle to pipe
chReply, // buffer to write from
cbReplyBytes, // number of bytes to write
&cbWritten, // number of bytes written
NULL); // not overlapped I/O

if (! fSuccess || cbReplyBytes != cbWritten) break;
}

// Flush the pipe to allow the client to read the pipe's contents
// before disconnecting. Then disconnect the pipe, and close the
// handle to this pipe instance.

FlushFileBuffers(hPipe);
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}

VOID GetAnswerToRequest(LPTSTR chRequest,
LPTSTR chReply, LPDWORD pchBytes)
{
_tprintf( TEXT("%s\n"), chRequest );
lstrcpy( chReply, TEXT("Default answer from server") );
*pchBytes = (lstrlen(chReply)+1)*sizeof(TCHAR);
}

Vincenzo1968
11-01-2013, 16:54
Non capisco... non vorrei che dipenda poi da visual studio... io ti posso assicurare che funziona ti metto il link del mio progetto, il progetto in C l'ho sviluppato con Blodsheed dev-c++

ecco il link https://www.4shared.com/rar/DjNlpwwZ/FIFO.html? forse devi essere registrato per scaricarlo, ma comunque la registrazione è gratuita

No no, il tuo codice mi funziona perfettamente. Il tuo client Java però non scrive nella pipe(legge soltanto in attesa dei messaggi dal server).

Nel client Java che ho copiato dal link invece si scrive nella pipe per inviare i dati al server. E bisognava utilizzare la codifica UTF-16 Little Endian.

Grazie mille per la tua disponibilità. :D