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

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

实际上有一篇关于在swig.org上包装sockaddr_in的文章,虽然它现在看起来有点老了。

基本上他们所做的就是编写一个为你创建一个新的sockaddr_in的函数,为需要填充的值提供参数,这些值很容易在Java中传递。 这是链接文章的略微更新,修剪版本:

 %module sock // Name of our module %{ #include  #include  #include  #include  #include  /* 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(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. 这些断言非常难看。

为了完整性,还有第三种可能的方法来包装它,它不涉及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(&$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()等效项,因此该案例可能应该有一个断言/exception。

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

在你的module.i中:

 %include "stdint.i" %{ #include  %} struct in_addr { uint32_t s_addr; }; struct sockaddr_in { uint16_t sin_port; struct in_addr sin_addr; }; 

使用java.net.InetSocketAddress。