1

I can't seem to find the answer anywhere, I'm trying to obtain a socket in Java, and hand over its file descriptor number so that I can use it in a C binary (the fd would be as argument).

I've obtained the FileDescriptor using reflection... but can't access the actual number anywhere.

I know other people have suggested JNI, but I'd like to keep it within Java if possible (and couldn't fully figure out how to do it)

pb2q
  • 58,613
  • 19
  • 146
  • 147
user1018513
  • 1,682
  • 1
  • 20
  • 42
  • 1
    AFAIK, you can't and even if you could, I don't see how you could "hand it over to" some other binary? Maybe if you posted what you are actually trying to do, there could be some alternatives? – Nim Jul 12 '12 at 16:09
  • 1
    I can, just done it and it works like a charm :) – user1018513 Jul 12 '12 at 16:36
  • It's generally meaningless to pass the descriptor number to another process - the same number in the other process doesn't necessarily refer to the same thing. How many processes on your system are using file descriptor `1` for `stdout` right now? Is `stdout` the same for all of them? – Dmitri Jul 12 '12 at 16:37
  • I was under the impression that calling the Exec method (Java Runtime) created a child process rather than a completely separate one, which would mean file descriptors would be shared. If that is not the case then I'll have to use JNI and the dup syscall. – user1018513 Jul 12 '12 at 16:56
  • Looks like it does something like a `fork()`/`exec()` (which inherits *copies* of the descriptors from the parent) but also closes everything but `stdin`,`stdout`, and `stderr` in the child. – Dmitri Jul 12 '12 at 17:24
  • It's unfortunate that Java Runtime doesn't support clone (unfortunate for my purpose anyways) – user1018513 Jul 13 '12 at 12:53

2 Answers2

4

In Java 7, you can cast a SocketInputStream to a FileInputStream, and call getFD() to get the FileDescriptor object.

Then you can use reflection to access the FileDescriptor object's private int fd field. (You use the Class.getDeclaredField(...) method to get the Field, call Field.setAccessible(true), and then get the field's value using Field.getInt(...).)


Beware that you may be making your code platform dependent by doing this. There are no guarantees that the particular private field will be present in older ... or forth-coming versions of Java, or in implementations of Java done by other vendors / suppliers.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • doesn't really help the OP... and anyways, AFAIK, this doesn't expose the underlying socket fd. – Nim Jul 12 '12 at 16:09
  • @Nim - You will have to ask him ... but that is my intention. And have you looked at the source code before you start making pronouncements that it won't work? (Hint: I have.) – Stephen C Jul 12 '12 at 16:15
  • That's exactly what I wanted, I already had the FileDescriptor object, I just needed the fd field, but was looking at a depecrated version of the API (must have been) since it didn't show any private int fd field ... That's the native file descriptor right? If yes then tahanks! – user1018513 Jul 12 '12 at 16:23
  • @user1018513 - you have to look at the source code. It is a private field, and private fields are not shown in the Java SE javadocs. I looked at the Java 7 version ... and this may be different in other versions. – Stephen C Jul 12 '12 at 16:41
  • You're right :). Grepcode is my friend from now on. Thanks. And the code is already horridly platform dependent ... but it'll do for now ! – user1018513 Jul 12 '12 at 16:47
  • 1
    I've got a Socket object that's just been returned from ServerSocketChannel#socket()#accept(). When I call getInputStream() on it and try to cast that to a FileInputStream, I get a ClassCastException that the SocketAdaptor can't be cast: sun.nio.ch.SocketAdaptor$SocketInputStream cannot be cast to java.io.FileInputStream" – MikeB May 26 '14 at 22:12
1

Stephen C's answer addresses how to get a FileDescriptor, but here's a method to the file descriptor number from that object. On Windows, FileDescriptor uses long handle instead of int fd internally, so this method first checks if handle is used and returns that if so, otherwise it falls back to returning fd. I haven't tested this with sockets as OP is using, but I imagine Windows JVMs still use handle.

public static long fileno(FileDescriptor fd) throws IOException {
    try {
        if (fd.valid()) {
            // windows builds use long handle
            long fileno = getFileDescriptorField(fd, "handle", false);
            if (fileno != -1) {
                return fileno;
            }
            // unix builds use int fd
            return getFileDescriptorField(fd, "fd", true);
        }
    } catch (IllegalAccessException e) {
        throw new IOException("unable to access handle/fd fields in FileDescriptor", e);
    } catch (NoSuchFieldException e) {
        throw new IOException("FileDescriptor in this JVM lacks handle/fd fields", e);
    }
    return -1;
}

private static long getFileDescriptorField(FileDescriptor fd, String fieldName, boolean isInt) throws NoSuchFieldException, IllegalAccessException {
    Field field = FileDescriptor.class.getDeclaredField(fieldName);
    field.setAccessible(true);
    long value = isInt ? field.getInt(fd) : field.getLong(fd);
    field.setAccessible(false);
    return value;
}
FThompson
  • 28,352
  • 13
  • 60
  • 93