4

我需要使用 JNA 将类似 BSD 的 C 套接字 API 包装到 Java。它具有与标准 BSD 套接字 API 基本相同的功能。

包装select()是有问题的,因为fd_set它的参数中需要 - 结构以及处理s所需的FD_*屏蔽函数(宏) 。fd_set我试图浏览头文件(例如 Ubuntu 8.04 中的 sys/select.h),但定义并不那么简单。特别是我发现很难找到FD_*-macros 的实现,当用 JNA 的 InvocationMapper 包装它们时需要它。

注意:我不是要包装标准的 TCP 或 unix-socket API,而是自定义的。因此,Java 中的内置套接字不符合要求。

4

2 回答 2

0

特别是我发现很难找到FD_*-macros 的实现,当用 JNA 的 InvocationMapper 包装它们时需要它。

C 预处理器cpp对于找出宏是如何扩展的很有用。编写一个使用相关宏的虚拟程序(它应该在词法上是正确的,但不需要编译),运行它cpp并观察会发生什么。

于 2009-08-20T10:00:33.387 回答
0

我对 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"); }}
于 2015-08-07T01:11:34.097 回答