2015-09-26 22 views
9

Ho un programma Linux diviso in due parti.In Java, come si ottiene un socket o DatagramSocket dal descrittore di file di un socket C già aperto?

Una parte attraversa NAT per ottenere un socket UDP (punzonatura UDP) o un socket TCP (punzonatura TCP). La prima parte è scritta in C per consentire funzionalità native che facilitano o migliorano il processo di attraversamento NAT. La seconda parte utilizza effettivamente la presa connessa ottenuta tramite l'attraversamento NAT eseguita nella prima parte.

Ora ecco il problema. Voglio che la prima parte, la parte che ottiene il socket, sia indipendente dalla seconda parte, la parte che utilizza il socket per uno scopo specifico dell'applicazione. Ad esempio, desidero che la prima parte sia riutilizzabile per una varietà di applicazioni diverse che hanno tutte bisogno di connessioni UDP e TCP stabilite tra pari.

In questo momento, vorrei che la seconda parte (la parte dell'applicazione) fosse scritta in Java anziché in C o C++. Voglio che la seconda parte utilizzi una connessione socket che è stata ottenuta dal codice C responsabile per NAT traversal. Diciamo che la prima parte stabilita una connessione e quindi restituisce una struct:

// Represents a TCP or UDP connection that was obtained in part one. 
struct ConnectionObtained { 
    int socket_file_descriptor; 
    int source_port; 
    int destination_port; 
    int source_address; // 4 byte ipv4 address 
    int destination_address; 
    int is_UDP; // 1 for UDP client socket, 0 for TCP client socket 
}; 

Il codice C nella prima parte in grado di fornire questo POD/struct per il codice Java in parte due o tramite JNI (Java Native Interface) o tramite comunicazione inter-processista.

Desidero che il codice Java utilizzi tali informazioni per costruire un oggetto il cui tipo dichiarato è java.net.DatagramSocket o java.net.Socket e quindi utilizzare tale oggetto ovunque sia previsto un DatagramSocket o un socket.

Come punto di partenza, si consideri il seguente codice di esempio ...

/** 
* Determines the Unix file descriptor number of the given {@link ServerSocket}. 
*/ 
private int getUnixFileDescriptor(ServerSocket ss) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { 
    Field $impl=ss.getClass().getDeclaredField("impl"); 
    $impl.setAccessible(true); 
    SocketImpl socketImpl=(SocketImpl)$impl.get(ss); 
    Method $getFileDescriptor=SocketImpl.class.getDeclaredMethod("getFileDescriptor"); 
    $getFileDescriptor.setAccessible(true); 
    FileDescriptor fd=(FileDescriptor)$getFileDescriptor.invoke(socketImpl); 
    Field $fd=fd.getClass().getDeclaredField("fd"); 
    $fd.setAccessible(true); 
    return (Integer)$fd.get(fd); 
} 

Il codice fa sembrare che può essere possibile a "ricrea un balzo {@link ServerSocket} sul dato descrittore di file. " Questo significa che è possibile "ricreare un limite {@link java.net.Socket} sul descrittore di file specificato"? Che dire di un limite {@link java.net.DatagramSocket}?

/** 
* Recreates a bound {@link ServerSocket} on the given file descriptor. 
*/ 
private ServerSocket recreateServerSocket(int fdn) throws Exception { 
    FileDescriptor fd=new FileDescriptor(); 
    Field $fd=FileDescriptor.class.getDeclaredField("fd"); 
    $fd.setAccessible(true); 
    $fd.set(fd,fdn); 
    Class $PlainSocketImpl=Class.forName("java.net.PlainSocketImpl"); 
    Constructor $init=$PlainSocketImpl.getDeclaredConstructor(FileDescriptor.class); 
    $init.setAccessible(true); 
    SocketImpl socketImpl=(SocketImpl)$init.newInstance(fd); 
    ServerSocket ss=new ServerSocket(); 
    ss.bind(new InetSocketAddress(0)); 
    Field $impl=ServerSocket.class.getDeclaredField("impl"); 
    $impl.setAccessible(true); 
    $impl.set(ss,socketImpl); 
    return ss; 
} 
+0

Se il codice Java deve essere interessato solo ai socket connessi, perché il codice Java è pieno di trucchi sul 'ServerSocket?' – EJP

+2

Se ho capito correttamente, si desidera utilizzare un socket aperto esistente come socket sottostante di un nuovo socket client Java? – perencia

+1

Possibile dupe: http://stackoverflow.com/questions/1243546/can-i-get-a-java-socket-from-a-file-descriptor-number –

risposta

0

È possibile trasferire file descriptor tra processi (almeno su sistemi POSIX) accroding Can I share a file descriptor to another process on linux or are they local to the process?

anche come accennato nei commenti (grazie a @Andrew Henle) si può incidere Java attraverso la riflessione e creare IO flussi per il descrittore di file esistente : Can I get a Java Socket from a file descriptor number? Inoltre è possibile estendere la classe Socket e sovrascrivere i metodi getInputStream/getOutputStream.

Ma in realtà suggerisco di utilizzare la prima parte come proxy. La parte Java apre il socket del server su localhost e quindi la parte C++ ritrasmette il traffico tra localhost e l'indirizzo WAN.

+0

"In realtà ti suggerisco di usare la prima parte come proxy". Quest'ultima frase era grammaticalmente scorretta. Dopo aver associato il socket del server TCP con Java e aver passato il socket nativo a C++, come si otterrà il socket client risultante su Java? –

+0

Non si dovrebbe trasferire il socket, è necessario trasferire il traffico: Client <---> Server C++ <---> server Java (<---> - connessione socket) – sibnick

3

Stai facendo due diverse domande. Puoi passare un socket associato dal codice C scritto in un processo separato e puoi passare un socket associato dal codice C scritto nello stesso processo.

Per la prima parte, no, non è possibile se il codice C è in un'applicazione e il codice Java è un altro perché, se fosse possibile, più applicazioni diverse sarebbero in grado di passare attorno a un socket (senza SCM_RIGHTS) . Uccidere l'applicazione che ha creato e associato inizialmente il socket creerebbe problemi per le altre applicazioni utilizzando/condividendo quel socket.

Per quanto riguarda il fatto che il codice C si trovi nella parte nativa di un'applicazione Java (ad esempio tramite jni), in tal caso il sistema operativo non sarebbe in grado di distinguere se il socket si trova nella parte Java del codice utente o la parte C, quindi non ti imbatti nel problema introdotto nel paragrafo precedente. È possibile passare un socket (descrittore di file int e descrittore di socket nativo) tra Java e il codice nativo (vedere link), ma questo non ti dice se sarebbe pratico in questo scenario.

Per quanto riguarda la creazione di un java.net.Socket o di un java.net.DatagramSocket da un descrittore di file socket associato proveniente da codice jni, non ne ho idea. Dovresti provare tu stesso.

+4

Non devi rispondere alla tua domanda come se fossi in terza persona. Potrebbe essere erroneamente interpretato come DID o in qualche modo schizofrenia programmatrice. E mi ha abbastanza confuso su una prima opinione che devo ammettere. – Malina

+0

Hahaha. In realtà mi parlo regolarmente, lol. Ma seriamente, ho pensato che qualcuno avrebbe più probabilità di pubblicare una risposta (buona/completa/funzionale) se ce n'era già una brutta/incompleta/non funzionale. –