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

发布于 2024-12-15 17:19:23 字数 95 浏览 0 评论 0 原文

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

I have a C function that I want to call using Java via SWIG but I'm unsure how to handle the sockaddr_in C structure. Anyone have any examples on how I can handle the sockaddr_in?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

荒芜了季节 2024-12-22 17:19:23

我确信有更好的答案,我期待看到它。但这似乎最初是有效的。

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

I'm sure there's a better answer, and I look forward to seeing it. But this seems to work initially.

In your 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;
};
情绪操控生活 2024-12-22 17:19:23

实际上,swig.orgsockaddr_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"

基本上,这会调用 getAddress() 和 getPort() 方法href="http://download.oracle.com/javase/1,5.0/docs/api/java/net/InetSocketAddress.html" rel="nofollow">java.net.InetSocketAddress< /a> 并使用结果为调用创建一个 struct sockaddr_in 。

注意:

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

为了完整起见,还有第三种可能的方法来包装它,它不涉及 JNI,但编写一些 Java 代码。我们所做的是让 SWIG 包装 struct sockaddr (如第一个示例所示),但随后让使用 sockaddr 的包装函数返回 java.net.InetSocketAddress< /code> 仍然对象并提供一些用于在两者之间进行转换的代码。我将给出一个带有“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 类型映射做类似的事情。这不能正确处理 AF_INET6 地址 - 我找不到 IPv6 地址的 InetAddress.getByAddress() 的等效项,因此可能应该有一个断言/异常那种情况。

There's actually an article on wrapping sockaddr_in on swig.org, although it looks slightly old now.

Basically what they did was write a function that creates a new sockaddr_in for you, taking arguments for the values that need to be filled in as things that are easy to pass around in Java. This is a slightly updated, trimmed version of the linked article:

%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);

There's a nicer way of wrapping this with SWIG though, we can write a typemap to use java.net.InetSocketAddress instead, which will feel far more "natural" on the Java side of the interface:

%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"

Basically this calls the getAddress() and getPort() methods of java.net.InetSocketAddress and uses the result to create a struct sockaddr_in for the call.

Notes:

  1. I'm not 100% sure I've got the byte order right here
  2. We ought to support AF_INET6 properly too - we'd need to inspect the given InetSocketAddress to see which sub-class it is in the typemap itself.
  3. There's no out typemap. This is basically the reverse procedure, the JNI code will create new Java objects for us.
  4. The asserts are pretty ugly.

For completeness there's also a third possible way of wrapping this, which involves no JNI, but writing a little bit of Java. What we do is have SWIG wrap the struct sockaddr as in the first example, but then have the wrapped functions that use sockaddr return a java.net.InetSocketAddress object still and supply some code for converting between the two. I'll give an example with an "out" typemap, i.e. for returning from functions.

Given:

sockaddr_in *make_stuff();

we can wrap it with:

%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);

We've specified how to wrap a sockaddr_in directly, but also instructed the return from the function itself to be the more appropriate Java type (%typemap(jstype)) and provided a small amount of Java to perform the conversion (%typemap(javaout)). We could do similar for an in typemap too. This doesn't handle AF_INET6 addresses properly - I can't find an equivalent of InetAddress.getByAddress() for IPv6 addresses, so there should probably be an assert/exception for that case.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文