Merge github.com:dicej/avian

This commit is contained in:
Joel Dice 2012-07-17 16:55:40 -06:00
commit 38e1e3f521
6 changed files with 469 additions and 6 deletions

View File

@ -257,7 +257,7 @@ setTcpNoDelay(JNIEnv* e, int d, bool on)
}
void
doListen(JNIEnv* e, int s, sockaddr_in* address)
doBind(JNIEnv* e, int s, sockaddr_in* address)
{
int opt = 1;
int r = ::setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
@ -272,8 +272,12 @@ doListen(JNIEnv* e, int s, sockaddr_in* address)
throwIOException(e);
return;
}
}
r = ::listen(s, 100);
void
doListen(JNIEnv* e, int s)
{
int r = ::listen(s, 100);
if (r != 0) {
throwIOException(e);
}
@ -333,6 +337,26 @@ doRead(int fd, void* buffer, size_t count)
#endif
}
int
doRecv(int fd, void* buffer, size_t count, int32_t* host, int32_t* port)
{
sockaddr address;
socklen_t length = sizeof(address);
int r = recvfrom
(fd, static_cast<char*>(buffer), count, 0, &address, &length);
if (r > 0) {
sockaddr_in a; memcpy(&a, &address, length);
*host = ntohl(a.sin_addr.s_addr);
*port = ntohs(a.sin_port);
} else {
*host = 0;
*port = 0;
}
return r;
}
int
doWrite(int fd, const void* buffer, size_t count)
{
@ -344,9 +368,9 @@ doWrite(int fd, const void* buffer, size_t count)
}
int
makeSocket(JNIEnv* e)
makeSocket(JNIEnv* e, int type = SOCK_STREAM, int protocol = IPPROTO_TCP)
{
int s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
int s = ::socket(AF_INET, type, protocol);
if (s < 0) {
throwIOException(e);
return s;
@ -378,7 +402,28 @@ Java_java_nio_channels_ServerSocketChannel_natDoListen(JNIEnv *e,
init(e, &address, host, port);
if (e->ExceptionCheck()) return 0;
::doListen(e, s, &address);
::doBind(e, s, &address);
if (e->ExceptionCheck()) return 0;
::doListen(e, s);
return s;
}
extern "C" JNIEXPORT jint JNICALL
Java_java_nio_channels_DatagramChannel_bind(JNIEnv *e,
jclass,
jstring host,
jint port)
{
int s = makeSocket(e, SOCK_DGRAM, IPPROTO_UDP);
if (s < 0) return s;
if (e->ExceptionCheck()) return 0;
sockaddr_in address;
init(e, &address, host, port);
if (e->ExceptionCheck()) return 0;
::doBind(e, s, &address);
return s;
}
@ -391,6 +436,16 @@ Java_java_nio_channels_SocketChannel_configureBlocking(JNIEnv *e,
setBlocking(e, socket, blocking);
}
extern "C" JNIEXPORT void JNICALL
Java_java_nio_channels_DatagramChannel_configureBlocking(JNIEnv* e,
jclass c,
jint socket,
jboolean blocking)
{
return Java_java_nio_channels_SocketChannel_configureBlocking
(e, c, socket, blocking);
}
extern "C" JNIEXPORT void JNICALL
Java_java_nio_channels_SocketChannel_natSetTcpNoDelay(JNIEnv *e,
jclass,
@ -423,6 +478,24 @@ Java_java_nio_channels_SocketChannel_natDoConnect(JNIEnv *e,
return s;
}
extern "C" JNIEXPORT jint JNICALL
Java_java_nio_channels_DatagramChannel_connect(JNIEnv *e,
jclass,
jstring host,
jint port)
{
int s = makeSocket(e, SOCK_DGRAM, IPPROTO_UDP);
if (e->ExceptionCheck()) return 0;
sockaddr_in address;
init(e, &address, host, port);
if (e->ExceptionCheck()) return 0;
::doConnect(e, s, &address);
return s;
}
extern "C" JNIEXPORT void JNICALL
Java_java_nio_channels_SocketChannel_natFinishConnect(JNIEnv *e,
jclass,
@ -475,6 +548,57 @@ Java_java_nio_channels_SocketChannel_natRead(JNIEnv *e,
return r;
}
extern "C" JNIEXPORT jint JNICALL
Java_java_nio_channels_DatagramChannel_receive(JNIEnv* e,
jclass,
jint socket,
jbyteArray buffer,
jint offset,
jint length,
jboolean blocking,
jintArray address)
{
int r;
int32_t host;
int32_t port;
if (blocking) {
uint8_t* buf = static_cast<uint8_t*>(allocate(e, length));
if (buf) {
r = ::doRecv(socket, buf, length, &host, &port);
if (r > 0) {
e->SetByteArrayRegion
(buffer, offset, r, reinterpret_cast<jbyte*>(buf));
}
free(buf);
} else {
return 0;
}
} else {
jboolean isCopy;
uint8_t* buf = static_cast<uint8_t*>
(e->GetPrimitiveArrayCritical(buffer, &isCopy));
r = ::doRecv(socket, buf + offset, length, &host, &port);
e->ReleasePrimitiveArrayCritical(buffer, buf, 0);
}
if (r < 0) {
if (eagain()) {
return 0;
} else {
throwIOException(e);
}
} else if (r == 0) {
return -1;
} else {
e->SetIntArrayRegion(address, 0, 1, &host);
e->SetIntArrayRegion(address, 1, 1, &port);
}
return r;
}
extern "C" JNIEXPORT jint JNICALL
Java_java_nio_channels_SocketChannel_natWrite(JNIEnv *e,
jclass,
@ -515,6 +639,18 @@ Java_java_nio_channels_SocketChannel_natWrite(JNIEnv *e,
return r;
}
extern "C" JNIEXPORT jint JNICALL
Java_java_nio_channels_DatagramChannel_write(JNIEnv* e,
jclass c,
jint socket,
jbyteArray buffer,
jint offset,
jint length,
jboolean blocking)
{
return Java_java_nio_channels_SocketChannel_natWrite
(e, c, socket, buffer, offset, length, blocking);
}
extern "C" JNIEXPORT void JNICALL
Java_java_nio_channels_SocketChannel_natThrowWriteError(JNIEnv *e,
@ -554,18 +690,29 @@ class Pipe {
address.sin_family = AF_INET;
address.sin_port = 0;
address.sin_addr.s_addr = inet_addr("127.0.0.1"); //INADDR_LOOPBACK;
listener_ = makeSocket(e);
if (e->ExceptionCheck()) return;
setBlocking(e, listener_, false);
::doListen(e, listener_, &address);
::doBind(e, listener_, &address);
if (e->ExceptionCheck()) return;
::doListen(e, listener_);
if (e->ExceptionCheck()) return;
socklen_t length = sizeof(sockaddr_in);
int r = getsockname(listener_, reinterpret_cast<sockaddr*>(&address),
&length);
if (r) {
throwIOException(e);
return;
}
writer_ = makeSocket(e);
if (e->ExceptionCheck()) return;
setBlocking(e, writer_, true);
connected_ = ::doConnect(e, writer_, &address);
}
@ -663,6 +810,7 @@ Java_java_nio_channels_SocketSelector_natInit(JNIEnv* e, jclass)
void *mem = malloc(sizeof(SelectorState));
if (mem) {
SelectorState *s = new (mem) SelectorState(e);
if (e->ExceptionCheck()) return 0;
if (s) {
FD_ZERO(&(s->read));

View File

@ -0,0 +1,19 @@
/* Copyright (c) 2012, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.net;
import java.io.IOException;
public abstract class DatagramSocket {
public abstract SocketAddress getRemoteSocketAddress();
public abstract void bind(SocketAddress address) throws SocketException;
}

View File

@ -0,0 +1,13 @@
/* Copyright (c) 2012, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.net;
public interface ProtocolFamily { }

View File

@ -0,0 +1,15 @@
/* Copyright (c) 2012, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.net;
public enum StandardProtocolFamily implements ProtocolFamily {
INET;
}

View File

@ -0,0 +1,180 @@
/* Copyright (c) 2012, Avian Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice appear
in all copies.
There is NO WARRANTY for this software. See license.txt for
details. */
package java.nio.channels;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.Socket;
import java.net.SocketException;
import java.net.DatagramSocket;
import java.net.StandardProtocolFamily;
public class DatagramChannel extends SelectableChannel
implements ReadableByteChannel, WritableByteChannel
{
public static final int InvalidSocket = -1;
private int socket = InvalidSocket;
private boolean blocking = true;
public SelectableChannel configureBlocking(boolean v) throws IOException {
blocking = v;
if (socket != InvalidSocket) {
configureBlocking(socket, v);
}
return this;
}
int socketFD() {
return socket;
}
void handleReadyOps(int ops) {
// ignore
}
public static DatagramChannel open(ProtocolFamily family)
throws IOException
{
if (family.equals(StandardProtocolFamily.INET)) {
Socket.init();
return new DatagramChannel();
} else {
throw new UnsupportedOperationException();
}
}
public static DatagramChannel open()
throws IOException
{
return open(StandardProtocolFamily.INET);
}
public DatagramSocket socket() {
return new Handle();
}
public DatagramChannel bind(SocketAddress address) throws IOException {
InetSocketAddress inetAddress;
try {
inetAddress = (InetSocketAddress) address;
} catch (ClassCastException e) {
throw new UnsupportedAddressTypeException();
}
socket = bind(inetAddress.getHostName(), inetAddress.getPort());
return this;
}
public DatagramChannel connect(SocketAddress address) throws IOException {
InetSocketAddress inetAddress;
try {
inetAddress = (InetSocketAddress) address;
} catch (ClassCastException e) {
throw new UnsupportedAddressTypeException();
}
socket = connect(inetAddress.getHostName(), inetAddress.getPort());
return this;
}
public int write(ByteBuffer b) throws IOException {
if (b.remaining() == 0) return 0;
byte[] array = b.array();
if (array == null) throw new NullPointerException();
int c = write
(socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking);
if (c > 0) {
b.position(b.position() + c);
}
return c;
}
public int read(ByteBuffer b) throws IOException {
int p = b.position();
receive(b);
return b.position() - p;
}
public SocketAddress receive(ByteBuffer b) throws IOException {
if (b.remaining() == 0) return null;
byte[] array = b.array();
if (array == null) throw new NullPointerException();
int[] address = new int[2];
int c = receive
(socket, array, b.arrayOffset() + b.position(), b.remaining(), blocking,
address);
if (c > 0) {
b.position(b.position() + c);
return new InetSocketAddress(ipv4ToString(address[0]), address[1]);
} else {
return null;
}
}
private static String ipv4ToString(int address) {
StringBuilder sb = new StringBuilder();
sb.append( address >> 24 ).append('.')
.append((address >> 16) & 0xFF).append('.')
.append((address >> 8) & 0xFF).append('.')
.append( address & 0xFF);
return sb.toString();
}
public class Handle extends DatagramSocket {
public SocketAddress getRemoteSocketAddress() {
throw new UnsupportedOperationException();
}
public void bind(SocketAddress address) throws SocketException {
try {
DatagramChannel.this.bind(address);
} catch (SocketException e) {
throw e;
} catch (IOException e) {
SocketException se = new SocketException();
se.initCause(e);
throw se;
}
}
}
private static native void configureBlocking(int socket, boolean blocking)
throws IOException;
private static native int bind(String hostname, int port)
throws IOException;
private static native int connect(String hostname, int port)
throws IOException;
private static native int write(int socket, byte[] array, int offset,
int length, boolean blocking)
throws IOException;
private static native int receive(int socket, byte[] array, int offset,
int length, boolean blocking,
int[] address)
throws IOException;
}

88
test/Datagrams.java Normal file
View File

@ -0,0 +1,88 @@
import java.net.SocketAddress;
import java.net.InetSocketAddress;
import java.net.ProtocolFamily;
import java.net.StandardProtocolFamily;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
public class Datagrams {
private static void expect(boolean v) {
if (! v) throw new RuntimeException();
}
private static boolean equal(byte[] a, int aOffset, byte[] b, int bOffset,
int length)
{
for (int i = 0; i < length; ++i) {
if (a[aOffset + i] != b[bOffset + i]) return false;
}
return true;
}
public static void main(String[] args) throws Exception {
final String Hostname = "localhost";
final int Port = 22043;
final SocketAddress Address = new InetSocketAddress(Hostname, Port);
final byte[] Message = "hello, world!".getBytes();
DatagramChannel out = DatagramChannel.open();
try {
out.configureBlocking(false);
out.connect(Address);
DatagramChannel in = DatagramChannel.open();
try {
in.configureBlocking(false);
in.socket().bind(Address);
Selector selector = Selector.open();
try {
SelectionKey outKey = out.register
(selector, SelectionKey.OP_WRITE, null);
SelectionKey inKey = in.register
(selector, SelectionKey.OP_READ, null);
int state = 0;
ByteBuffer inBuffer = ByteBuffer.allocate(Message.length);
loop: while (true) {
selector.select();
switch (state) {
case 0: {
if (outKey.isWritable()) {
out.write(ByteBuffer.wrap(Message));
state = 1;
}
} break;
case 1: {
if (inKey.isReadable()) {
in.receive(inBuffer);
if (! inBuffer.hasRemaining()) {
expect(equal(inBuffer.array(),
inBuffer.arrayOffset(),
Message,
0,
Message.length));
break loop;
}
}
} break;
default: throw new RuntimeException();
}
}
} finally {
selector.close();
}
} finally {
in.close();
}
} finally {
out.close();
}
}
}