2015-09-26 7 views
9

Mam program Linux podzielony na dwie części.W Javie, w jaki sposób uzyskać Socket lub DatagramSocket z deskryptora pliku już otwartego gniazda C?

Jedna część wykonuje translację NAT w celu uzyskania gniazda UDP (wykrywanie otworów UDP) lub gniazda TCP (dziurkowanie TCP). Część pierwsza została napisana w języku C, aby umożliwić natywne funkcje, które ułatwiają lub usprawniają proces translacji NAT. Część druga w rzeczywistości korzysta z podłączonego gniazda uzyskanego przez translację NAT wykonaną w części pierwszej.

Tutaj jest problem. Chcę, aby pierwsza część, część, która uzyskuje gniazdo, była niezależna od drugiej części, części, która używa gniazda do konkretnego zastosowania aplikacji. Na przykład chcę, aby pierwsza część była wielokrotnego użytku dla wielu różnych aplikacji, które wszystkie wymagają połączeń UDP i TCP, które zostały ustanowione między równorzędnymi.

W tej chwili chciałbym, aby druga część (część aplikacji) została napisana w Javie zamiast w C lub C++. Chcę, aby druga część używała połączenia z gniazdem, które zostało uzyskane przez kod C odpowiedzialny za przechodzenie NAT. Powiedzmy, że pierwsza część powstała z połączenia, a następnie zwraca 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 
}; 

Kod C w jednej części może zapewnić tę POD/struct do kodu Java w drugiej części za pośrednictwem JNI (Java Native Interface) lub poprzez komunikacja międzyprocesowa.

Chcę, aby kod Java użył tych informacji do skonstruowania obiektu, którego zadeklarowanym typem jest albo java.net.DatagramSocket lub java.net.Socket, a następnie użyj tego obiektu wszędzie tam, gdzie oczekiwano DatagramSocket lub Socket.

Jako punkt wyjścia, należy rozważyć następujący przykładowy kod ...

/** 
* 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); 
} 

Kod sprawia, że ​​wydaje się, że może to być możliwe, aby „odtwarza związanego {@link ServerSocket} na podanym deskryptorze pliku. " Czy to oznacza, że ​​możliwe jest "odtworzenie powiązanego {@link java.net.Socket} na podanym deskryptorze pliku"? A co ze związanym {@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

Jeśli kod Java musi dotyczyć jedynie podłączonych gniazd, dlaczego Twój kod Java jest pełen trików na 'ServerSocket?' – EJP

+2

Jeśli dobrze zrozumiałem, czy chcesz użyć istniejącego otwartego gniazda jako podstawowego gniazda nowe gniazdo klienta Java? – perencia

+1

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

Odpowiedz

0

można przesyłać deskryptor pliku między procesami (przynajmniej na systemach POSIX) accroding Can I share a file descriptor to another process on linux or are they local to the process?

również wspomniano w komentarzach (dzięki @Andrew Henle) można siekać Java poprzez refleksję i tworzyć IO strumieni dla istniejącego deskryptora pliku : Can I get a Java Socket from a file descriptor number? Możliwe jest również rozszerzenie klasy Socket i nadpisanie metod getInputStream/getOutputStream.

Ale tak naprawdę proponuję użyć pierwszej części jako proxy. Część Java po prostu otwiera gniazdo serwera na localhost, a następnie część C++ przekazuje retransmity między localhost i WAN address.

+0

"Ale tak naprawdę proponuję użyć pierwszej części jako proxy". To ostatnie zdanie było niepoprawne gramatycznie. Po powiązaniu gniazda serwera TCP z Javą i przekazaniu rodzimego gniazda do C++, w jaki sposób otrzymasz wynikowe gniazdo klienta z powrotem do Javy? –

+0

Nie należy przenosić gniazda, należy przenieść ruch: Klient <---> Serwer C++ <---> Serwer Java (<---> - połączenie z gniazdem) – sibnick

3

Zadajesz dwa różne pytania. Czy możesz przekazać oprawione gniazdo z kodu C napisanego w oddzielnym procesie i możesz przekazać oprawione gniazdo z kodu C napisanego w tym samym procesie.

Dla pierwszej części, nie, nie jest możliwe, jeśli kod C jest w jednej aplikacji, a kod Java jest inny, ponieważ gdyby było to możliwe, wiele różnych aplikacji byłoby w stanie ominąć gniazdo (bez SCM_RIGHTS) . Zabicie aplikacji, która utworzyła i związała początkowo gniazdo, powodowałoby problemy dla innych aplikacji używających/dzielących to gniazdo.

Co do posiadania kodu C w natywnej części aplikacji Java (np. Przez jni), w takim przypadku system operacyjny nie będzie w stanie odróżnić, czy gniazdo znajduje się w części Java kodu użytkownika lub część C, więc nie napotkasz problemu wprowadzonego w poprzednim paragrafie. Możliwe jest przekazanie gniazda (deskryptor pliku int i deskryptor gniazda) między Javą i kodem rodzimym (patrz link), ale to nie mówi, czy byłoby to praktyczne w tym scenariuszu.

Jeśli chodzi o tworzenie java.net.Socket lub java.net.DatagramSocket z deskryptora pliku socket, który pochodzi z kodu JNI, nie mam pojęcia. Musiałbyś spróbować sam.

+4

Nie musisz odpowiadać na swoje pytanie tak, jakbyś był trzecią osobą. Może być źle interpretowane jako DID lub nieco schizofrenii programisty. I to całkiem zdezorientowało mnie na pierwszym widoku, który muszę przyznać. – Malina

+0

Hahaha. Właściwie mówię do siebie regularnie, lol. Ale poważnie, myślałem, że ktoś będzie bardziej prawdopodobne, aby opublikować odpowiedź (dobra/pełna/funkcjonalna), jeśli był tam już zły/niekompletny/niefunkcjonalny. –

Powiązane problemy