Wrapping BSD select () with JNA

I need to wrap a BS-like socket API in Java using JNA. It has basically the same functions as the standard BSD socket API.

The wrap around select() is problematic because of the fd_set structure required in its arguments and the FD_ FD_* masking functions (macros) that are needed to handle fd_set s. I tried to scan the header files (e.g. sys / select.h in Ubuntu 8.04), but the definitions are not so simple. It was especially difficult for me to find the implementation of FD_* -macros, which is necessary when transferring them using the JNA InvocationMapper.

Note. I am not trying to wrap a standard TCP or unix socket API, but another. Thus, the built-in sockets in Java do not match the count.

+4
source share
2 answers

In particular, it was difficult for me to find the implementation of FD_* -macros, which is necessary when transferring them using the JNA InvocationMapper.

The C cpp preprocessor is useful for figuring out how macros expand. Write a dummy program that uses the appropriate macros (it should be lexically correct, but not necessarily compiled), run it through cpp and see what happens.

0
source

I use an byte array for the fd_set structure and some arithmetic to find the position of the right byte in the array:

 private static final int FD_SETSIZE = 1024; private static final boolean isBigEndian = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN; private static interface Libc extends Library { int select (int nfds, byte[] readfds, byte[] writefds, byte[] errfds, TimeVal timeout); //... } private static class FdSet { byte[] a; FdSet() { a = new byte[FD_SETSIZE / 8]; } void set (int fd) { a[getBytePos(fd)] |= getBitMask(fd); } boolean isSet (int fd) { return (a[getBytePos(fd)] & getBitMask(fd)) != 0; } private static int getBytePos (int fd) { if (fd < 0 || fd >= LibcDefs.FD_SETSIZE) { throw new RuntimeException("File handle out of range for fd_set."); } if (isBigEndian) { return (fd / 8 / Native.LONG_SIZE + 1) * Native.LONG_SIZE - 1 - fd / 8 % Native.LONG_SIZE; } else { return fd / 8; }} private static int getBitMask (int fd) { return 1 << (fd % 8); }} private static class TimeVal extends Structure { public NativeLong tv_sec; public NativeLong tv_usec; TimeVal (int ms) { set(ms); } void set (int ms) { tv_sec.setValue(ms / 1000); tv_usec.setValue(ms % 1000 * 1000); } @Override protected List<?> getFieldOrder() { return Arrays.asList("tv_sec", "tv_usec"); }} public boolean waitInputReady (int timeoutMs) throws IOException { TimeVal timeVal = (timeoutMs < 0) ? null : new TimeVal(timeoutMs); FdSet rxSet = new FdSet(); FdSet errorSet = new FdSet(); rxSet.set(fileHandle); errorSet.set(fileHandle); int rc = libc.select(fileHandle + 1, rxSet.a, null, errorSet.a, timeVal); checkSelectErrors(rc, errorSet); if (rc == 0) { return false; } if (!rxSet.isSet(fileHandle)) { throw new RuntimeException("rxSet bit is not set after select()."); } return true; } public boolean waitOutputReady (int timeoutMs) throws IOException { TimeVal timeVal = (timeoutMs < 0) ? null : new TimeVal(timeoutMs); FdSet txSet = new FdSet(); FdSet errorSet = new FdSet(); txSet.set(fileHandle); errorSet.set(fileHandle); int rc = libc.select(fileHandle + 1, null, txSet.a, errorSet.a, timeVal); checkSelectErrors(rc, errorSet); if (rc == 0) { return false; } if (!txSet.isSet(fileHandle)) { throw new RuntimeException("txSet bit is not set after select()."); } return true; } private void checkSelectErrors (int rc, FdSet errorSet) throws IOException { if (rc == -1) { throw new IOException("Error in select(), errno=" + Native.getLastError() + "."); } boolean error = errorSet.isSet(fileHandle); if (!(rc == 0 && !error || rc == 1 || rc == 2 && error)) { throw new RuntimeException("Invalid return code received from select(), rc=" + rc + ", error=" + error + "."); } if (error) { throw new IOException("Channel error state detected"); }} 
0
source

All Articles