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?
2 Answers
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:
- I'm not 100% sure I've got the byte order right here
- 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. - There's no
out
typemap. This is basically the reverse procedure, the JNI code will create new Java objects for us. - 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.

- 87,323
- 22
- 191
- 272
-
-
@c12 I have an idea for a much better way that I'll try and find time to flesh out – Flexo Nov 16 '11 at 18:22
-
thanks...I'm going to post another question with some more specifics about my situation that I didn't state originally that maybe you can address and get credit for – c12 Nov 16 '11 at 18:58
-
http://stackoverflow.com/questions/8157401/change-swig-interface-file-to-allow-return-of-object-type-from-function – c12 Nov 16 '11 at 19:20
-
@c12 - I've added a proper typemap for this that makes functions take a normal `InetSocketAddress` as input now instead of a wrapped type. – Flexo Nov 19 '11 at 01:31
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;
};

- 11
- 1