Обертывание BSD select () с помощью JNA - PullRequest
4 голосов
/ 20 августа 2009

Мне нужно обернуть BSD-подобный API-интерфейс сокета C для Java с помощью JNA. Он имеет в основном те же функции, что и стандартный API сокетов BSD.

Обтекание select() проблематично из-за структуры fd_set, требуемой в его аргументах, и маскирующих функций (макросов) FD_*, необходимых для обработки fd_set с. Я попытался пролистать файлы заголовков (например, sys / select.h в Ubuntu 8.04), но определения не так просты. Особенно мне было трудно найти реализацию FD_* -macros, которая необходима при обертывании их с помощью InvocationMapper JNA.

Примечание: я не пытаюсь обернуть стандартный TCP или API Unix-сокета, а пользовательский. Таким образом, встроенные сокеты в Java не отвечают всем требованиям.

Ответы [ 2 ]

0 голосов
/ 07 августа 2015

Я использую байтовый массив для структуры fd_set и некоторую арифметику, чтобы найти правильную позицию байта в массиве:

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 голосов
/ 20 августа 2009

Особенно мне было трудно найти реализацию FD_* -macros, которая необходима при их обёртывании в InvocationMapper JNA.

Препроцессор C cpp полезен, чтобы узнать, как макросы раскрываются. Напишите фиктивную программу, которая использует соответствующие макросы (она должна быть лексически правильной, но не должна компилироваться), запустите ее через cpp и посмотрите, что произойдет.

...