如何使用SWIG将sockaddr_in C结构映射到Java

时间:2011-11-15 19:30:55

标签: java swig

我有一个C函数,我想通过SWIG使用Java调用,但我不确定如何处理sockaddr_in C结构。任何人都有关于如何处理sockaddr_in的例子吗?

3 个答案:

答案 0 :(得分:2)

实际上有一篇关于在swig.org上包裹sockaddr_in的文章,虽然现在看起来略显陈旧。

基本上他们所做的就是编写一个函数,为你创建一个新的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"

基本上,这会调用java.net.InetSocketAddressgetAddress()getPort()方法,并使用结果为调用创建struct sockaddr_in

注意:

  1. 我不是100%确定我在这里有字节顺序
  2. 我们也应该正确支持AF_INET6 - 我们需要检查给定的InetSocketAddress以查看它在typemap本身中的哪个子类。
  3. 没有out类型地图。这基本上是相反的过程,JNI代码将为我们创建新的Java对象。
  4. 断言非常难看。

  5. 为了完整性,还有第三种可能的方法来包装它,它不涉及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))。我们也可以在类型图中做类似的事情。这不能正确处理AF_INET6地址 - 我找不到IPv6地址的等效InetAddress.getByAddress(),所以对于那种情况应该有一个断言/异常。

答案 1 :(得分:1)

我确信有更好的答案,我期待着看到它。但这似乎最初起作用。

在你的module.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;
};

答案 2 :(得分:-1)

使用java.net.InetSocketAddress。