Как отобразить структуру sockaddr_in C на Java с помощью SWIG - PullRequest
1 голос
/ 15 ноября 2011

У меня есть функция C, которую я хочу вызвать с использованием Java через SWIG, но я не уверен, как обращаться со структурой C sockaddr_in.У кого-нибудь есть примеры того, как я могу справиться с sockaddr_in?

Ответы [ 3 ]

2 голосов
/ 16 ноября 2011

На самом деле есть статья об упаковке sockaddr_in на swig.org , хотя сейчас она выглядит немного старой.

По сути, они написали функцию, которая создает новый sockaddr_in для вас, принимая аргументы для значений, которые должны быть заполнены как вещи, которые легко передать в Java.Это немного обновленная, урезанная версия связанной статьи:

%module sock          // Name of our module
%{
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

/* Set some values in the sockaddr_in structure */
struct sockaddr *new_sockaddr_in(short family, unsigned long hostid, int port) {
        struct sockaddr_in *addr;
        addr = (struct sockaddr_in *) malloc(sizeof(struct sockaddr_in));
        bzero((char *) addr, sizeof(struct sockaddr_in));
        addr->sin_family = family;
        addr->sin_addr.s_addr = hostid;
        addr->sin_port = htons(port);
        return (struct sockaddr *) addr;
}
%}

// Add these constants
enum {AF_UNIX, AF_INET, SOCK_STREAM, SOCK_DGRAM, SOCK_RAW,
      IPPROTO_UDP, IPPROTO_TCP, INADDR_ANY};

#define  SIZEOF_SOCKADDR  sizeof(struct sockaddr)

// Wrap these functions
struct sockaddr *new_sockaddr_in(short family, unsigned long, int port);

Хотя есть более приятный способ обернуть это SWIG, мы можем написать карту типов для использования java.net.InetSocketAddress, которая будетчувствовать себя более «естественным» на стороне интерфейса Java:

%typemap(jni) sockaddr_in *ADDR "jobject"
%typemap(jtype) sockaddr_in *ADDR "java.net.InetSocketAddress"
%typemap(jstype) sockaddr_in *ADDR "java.net.InetSocketAddress"

%typemap(in) (sockaddr_in *ADDR) {
  $1 = new sockaddr_in;
  $1->sin_family = AF_INET;
  jclass inetsockaddr = jenv->FindClass("java/net/InetSocketAddress");
  assert(inetsockaddr);
  // TODO: check return
  jmethodID pmid,addrmid,ipbytemid;
  pmid = jenv->GetMethodID(inetsockaddr, "getPort", "()I");
  assert(pmid);
  jint port = jenv->CallIntMethod($input, pmid);
  $1->sin_port = htons(port);
  jclass inetaddr = jenv->FindClass("java/net/InetAddress");
  assert(inetaddr);
  addrmid = jenv->GetMethodID(inetsockaddr, "getAddress", "()Ljava/net/InetAddress;");
  assert(addrmid);
  jobject addrobj = jenv->CallObjectMethod($input, addrmid);
  assert(addrobj);
  ipbytemid = jenv->GetMethodID(inetaddr, "getAddress", "()[B");
  assert(ipbytemid);
  jbyteArray barr = static_cast<jbyteArray>(jenv->CallObjectMethod(addrobj, ipbytemid));
  assert(barr);
  jbyte *bytes = jenv->GetByteArrayElements(barr, 0);
  assert(bytes);
  memcpy(&$1->sin_addr.s_addr, bytes, 4);
  $1->sin_addr.s_addr = htonl($1->sin_addr.s_addr);
  jenv->ReleaseByteArrayElements(barr, bytes, JNI_ABORT); // No changes copied back
}

%typemap(freearg) (sockaddr_in *ADDR) {
  delete $1;
}

%typemap(javain) sockaddr_in *ADDR "$javainput"

В основном это вызывает getAddress() и getPort() методы java.net.InetSocketAddress и использует результат длясоздайте struct sockaddr_in для вызова.

Примечания:

  1. Я не уверен на 100%, у меня есть порядок байтов прямо здесь
  2. Мы должныдля правильной поддержки AF_INET6 - нам нужно проверить заданный InetSocketAddress, чтобы увидеть, какой подкласс находится в самой карте типов.
  3. Там нет out карты типов.Это в основном обратная процедура, код JNI создаст для нас новые объекты Java.
  4. Утверждения довольно уродливы.

Для полноты есть и третий возможный способобернуть это, что не требует JNI, но немного написания на Java.Мы делаем SWIG-обертку struct sockaddr, как в первом примере, но затем имеем обернутые функции, которые используют sockaddr, возвращают объект java.net.InetSocketAddress и предоставляют некоторый код для преобразования между ними.Я приведу пример с типовой картой «out», то есть для возврата из функций.

Учитывая:

sockaddr_in *make_stuff();

, мы можем обернуть его:

%typemap(jstype) sockaddr_in *make_stuff "java.net.InetSocketAddress"
%typemap(javaout) sockaddr_in *make_stuff {
  long cPtr = $jnicall;
  sockaddr_in s = new sockaddr_in(cPtr, true);
  byte[] bytes = new byte[4];
  for (int i = 0; i < 4; ++i) {
    bytes[i] = (byte)s.getAddr(i);
  }
  java.net.InetAddress addr = null;
  try {
    addr = java.net.InetAddress.getByAddress(bytes);
  }
  catch (java.net.UnknownHostException e) {
    return null;
  }
  return new java.net.InetSocketAddress(addr, s.getPort());
}

%immutable;
struct sockaddr_in{
   %rename(family) sin_family;
   short sin_family;
   %extend {
     unsigned short getPort() const {
       return ntohs($self->sin_port);
     }
     char getAddr(int byte) const {
       const char *ptr = reinterpret_cast<const char*>(&$self->sin_addr.s_addr);
       return ptr[byte];
     }
   }
};
%mutable;

void do_stuff(sockaddr_in *ADDR);

Мы указали, как обернуть sockaddr_in напрямую, но также указали, что возврат из самой функции должен быть более подходящим типом Java (%typemap(jstype)), и предоставили небольшое количество Java для выполнения преобразования (%typemap(javaout)).).Мы могли бы сделать то же самое для in typemap тоже.Это не обрабатывает AF_INET6 адреса должным образом - я не могу найти эквивалент InetAddress.getByAddress() для адресов IPv6, поэтому, вероятно, должно быть утверждение / исключение для этого случая.

1 голос
/ 16 ноября 2011

Я уверен, что есть лучший ответ, и я с нетерпением жду его увидеть. Но, похоже, это сработало изначально.

В вашем модуле .i:

%include "stdint.i"

%{
#include <arpa/inet.h>
%}

struct in_addr {
    uint32_t s_addr;
};

struct sockaddr_in {
    uint16_t sin_port;
    struct in_addr sin_addr;
};
0 голосов
/ 16 ноября 2011

Использовать java.net.InetSocketAddress.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...