Implement socket API

This commit is contained in:
Ilya Mizus 2013-11-05 00:07:43 +03:00 committed by Joshua Warner
parent 2800ffe826
commit 45ee25f68c
10 changed files with 623 additions and 56 deletions

View File

@ -9,33 +9,70 @@
details. */
#include "jni.h"
#include "jni-util.h"
#include "avian/machine.h"
#ifdef PLATFORM_WINDOWS
# include <winsock2.h>
# define ONLY_ON_WINDOWS(x) x
#else
# include <netdb.h>
# include <sys/socket.h>
# include <netinet/in.h>
# define ONLY_ON_WINDOWS(x)
#endif
#include "sockets.h"
using namespace avian::classpath::sockets;
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_init(JNIEnv* ONLY_ON_WINDOWS(e), jclass)
{
#ifdef PLATFORM_WINDOWS
static bool wsaInitialized = false;
if (not wsaInitialized) {
WSADATA data;
int r = WSAStartup(MAKEWORD(2, 2), &data);
if (r or LOBYTE(data.wVersion) != 2 or HIBYTE(data.wVersion) != 2) {
throwNew(e, "java/io/IOException", "WSAStartup failed");
} else {
wsaInitialized = true;
}
}
#endif
Java_java_net_Socket_init(JNIEnv* e, jclass) {
init(e);
}
extern "C" JNIEXPORT SOCKET JNICALL
Java_java_net_Socket_create(JNIEnv* e, jclass) {
return create(e);
}
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_connect(JNIEnv* e, jclass, SOCKET sock, long addr, short port) {
connect(e, sock, addr, port);
}
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_bind(JNIEnv* e, jclass, SOCKET sock, long addr, short port) {
bind(e, sock, addr, port);
}
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_abort(JNIEnv* e, jclass, SOCKET sock) {
abort(e, sock);
}
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_close(JNIEnv* e, jclass, SOCKET sock) {
close(e, sock);
}
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_closeOutput(JNIEnv* e, jclass, SOCKET sock) {
close_output(e, sock);
}
extern "C" JNIEXPORT void JNICALL
Java_java_net_Socket_closeInput(JNIEnv* e, jclass, SOCKET sock) {
close_input(e, sock);
}
extern "C" JNIEXPORT void JNICALL
Avian_java_net_Socket_send(vm::Thread* t, vm::object, uintptr_t* arguments) { /* SOCKET s, object buffer_obj, int start_pos, int count */
SOCKET& s = *(reinterpret_cast<SOCKET*>(&arguments[0]));
vm::object buffer_obj = reinterpret_cast<vm::object>(arguments[2]);
int32_t& start_pos = *(reinterpret_cast<int32_t*>(&arguments[3]));
int32_t& count = *(reinterpret_cast<int32_t*>(&arguments[4]));
char* buffer = reinterpret_cast<char*>(&vm::byteArrayBody(t, buffer_obj, start_pos));
avian::classpath::sockets::send((JNIEnv*)t, s, buffer, count);
}
extern "C" JNIEXPORT int64_t JNICALL
Avian_java_net_Socket_recv(vm::Thread* t, vm::object, uintptr_t* arguments) { /* SOCKET s, object buffer_obj, int start_pos, int count */
SOCKET& s = *(reinterpret_cast<SOCKET*>(&arguments[0]));
vm::object buffer_obj = reinterpret_cast<vm::object>(arguments[2]);
int32_t& start_pos = *(reinterpret_cast<int32_t*>(&arguments[3]));
int32_t& count = *(reinterpret_cast<int32_t*>(&arguments[4]));
char* buffer = reinterpret_cast<char*>(&vm::byteArrayBody(t, buffer_obj, start_pos));
return avian::classpath::sockets::recv((JNIEnv*)t, s, buffer, count);
}
extern "C" JNIEXPORT jint JNICALL
@ -49,7 +86,7 @@ Java_java_net_InetAddress_ipv4AddressForName(JNIEnv* e,
hostent* host = gethostbyname(chars);
e->ReleaseStringUTFChars(name, chars);
if (host) {
return htonl(reinterpret_cast<in_addr*>(host->h_addr_list[0])->s_addr);
return ntohl(reinterpret_cast<in_addr*>(host->h_addr_list[0])->s_addr);
} else {
fprintf(stderr, "trouble %d\n", WSAGetLastError());
}
@ -67,7 +104,7 @@ Java_java_net_InetAddress_ipv4AddressForName(JNIEnv* e,
if (r != 0) {
address = 0;
} else {
address = htonl
address = ntohl
(reinterpret_cast<sockaddr_in*>(result->ai_addr)->sin_addr.s_addr);
freeaddrinfo(result);
@ -78,3 +115,4 @@ Java_java_net_InetAddress_ipv4AddressForName(JNIEnv* e,
}
return 0;
}

View File

@ -0,0 +1,15 @@
/* Copyright (c) 2008-2013, 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.lang;
public interface AutoCloseable {
void close() throws Exception;
}

View File

@ -123,4 +123,7 @@ public class Throwable implements Serializable {
trace = trace(0);
return this;
}
public void addSuppressed(Throwable exception) {
}
}

View File

@ -13,41 +13,61 @@ package java.net;
import java.io.IOException;
public class InetAddress {
private final String address;
private final String name;
private final int ip;
private InetAddress(String address) {
this.address = address;
private InetAddress(String name) throws UnknownHostException {
this.name = name;
this.ip = ipv4AddressForName(name);
if (ip == 0) {
throw new UnknownHostException(name);
}
}
public String getHostName() {
return name;
}
public String getHostAddress() {
return address;
try {
return new InetAddress(name).toString();
} catch (UnknownHostException e) {
return null; // Strange case
}
}
public static InetAddress getByName(String name)
throws UnknownHostException
{
public static InetAddress getByName(String name) throws UnknownHostException {
try {
Socket.init();
return new InetAddress(name);
} catch (IOException e) {
UnknownHostException uhe = new UnknownHostException(name);
uhe.initCause(e);
throw uhe;
}
int address = ipv4AddressForName(name);
if (address == 0) {
throw new UnknownHostException(name);
} else {
return new InetAddress(ipv4AddressToString(address));
}
}
private static String ipv4AddressToString(int address) {
return (((address >>> 24) ) + "." +
((address >>> 16) & 0xFF) + "." +
((address >>> 8 ) & 0xFF) + "." +
((address ) & 0xFF));
public byte[] getAddress() {
byte[] res = new byte[4];
res[0] = (byte) ( ip >>> 24);
res[1] = (byte) ((ip >>> 16) & 0xFF);
res[2] = (byte) ((ip >>> 8 ) & 0xFF);
res[3] = (byte) ((ip ) & 0xFF);
return res;
}
private static native int ipv4AddressForName(String name);
@Override
public String toString() {
byte[] addr = getAddress();
return (int)((addr[0] + 256) % 256) + "." +
(int)((addr[1] + 256) % 256) + "." +
(int)((addr[2] + 256) % 256) + "." +
(int)((addr[3] + 256) % 256);
}
int getRawAddress() {
return ip;
}
static native int ipv4AddressForName(String name);
}

View File

@ -11,16 +11,25 @@
package java.net;
public class InetSocketAddress extends SocketAddress {
private final String host;
private final InetAddress address;
private final int port;
public InetSocketAddress(String host, int port) {
this.host = host;
public InetSocketAddress(String host, int port) throws UnknownHostException {
this.address = InetAddress.getByName(host);
this.port = port;
}
public InetSocketAddress(InetAddress address, int port) {
this.address = address;
this.port = port;
}
public InetAddress getAddress() {
return address;
}
public String getHostName() {
return host;
return address.getHostName();
}
public int getPort() {

View File

@ -10,10 +10,191 @@
package java.net;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public abstract class Socket {
public static native void init() throws IOException;
public class Socket implements Closeable, AutoCloseable {
public abstract void setTcpNoDelay(boolean on) throws SocketException;
private static final int SD_RECEIVE = 0x00;
private static final int SD_SEND = 0x01;
private static final int SD_BOTH = 0x02;
private static final int BUFFER_SIZE = 65535;
/**
* This method is called from all routines that depend on winsock in windows,
* so it has public visibility
* @throws IOException
*/
public static native void init() throws IOException;
/**
* Creates the native socket object
* @return Handle to the native object
* @throws IOException
*/
private static native /* SOCKET */long create() throws IOException;
/**
* Connects the native socket object to an address:port
* @param sock Native socket handler
* @param addr Address to connect to
* @param port Port to connect to
* @throws IOException
*/
private static native void connect(/* SOCKET */long sock, long addr, short port) throws IOException;
private static native void bind(/* SOCKET */long sock, long addr, short port) throws IOException;
private static native void send(/* SOCKET */long sock, byte[] buffer, int start_pos, int count) throws IOException;
private static native int recv(/* SOCKET */long sock, byte[] buffer, int start_pos, int count) throws IOException;
private static native void abort(/* SOCKET */long sock);
private static native void close(/* SOCKET */long sock);
private static native void closeOutput(/* SOCKET */long sock);
private static native void closeInput(/* SOCKET */long sock);
private class SocketInputStream extends InputStream {
private boolean closed = false;
@Override
public void close() throws IOException {
if (!closed) {
closeInput(sock);
closed = true;
}
super.close();
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
@Override
public int read() throws IOException {
byte[] buffer = new byte[1];
int size = recv(sock, buffer, 0, 1);
if (size == 0) {
return -1;
}
return buffer[0];
}
@Override
public int read(byte[] buffer) throws IOException {
int fullSize = buffer.length;
int index = 0;
int size;
do {
size = recv(sock, buffer, index, Math.min(fullSize, Socket.BUFFER_SIZE));
fullSize -= size;
index += size;
} while (fullSize != 0 && size != 0);
return index;
}
}
private class SocketOutputStream extends OutputStream {
private boolean closed = false;
@Override
public void close() throws IOException {
if (!closed) {
closeOutput(sock);
closed = true;
}
super.close();
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
@Override
public void write(int c) throws IOException {
byte[] res = new byte[1];
res[0] = (byte)c;
send(sock, res, 0, 1);
}
@Override
public void write(byte[] buffer) throws IOException {
int fullSize = buffer.length;
int index = 0;
int size;
do {
size = Math.min(fullSize, Socket.BUFFER_SIZE);
send(sock, buffer, index, size);
fullSize -= size;
index += size;
} while (fullSize != 0 && size != 0);
}
}
private long sock;
private SocketInputStream inputStream;
private SocketOutputStream outputStream;
public Socket() throws IOException {
Socket.init();
sock = create();
inputStream = new SocketInputStream();
outputStream = new SocketOutputStream();
}
public SocketInputStream getInputStream() {
return inputStream;
}
public SocketOutputStream getOutputStream() {
return outputStream;
}
public Socket(InetAddress address, int port) throws IOException {
this();
connect(sock, address.getRawAddress(), (short)port);
}
public Socket(String host, int port) throws UnknownHostException, IOException {
this(InetAddress.getByName(host), port);
}
public void bind(SocketAddress bindpoint) throws IOException {
if (bindpoint instanceof InetSocketAddress) {
InetSocketAddress inetBindpoint = (InetSocketAddress)bindpoint;
bind(sock, inetBindpoint.getAddress().getRawAddress(), (short) inetBindpoint.getPort());
}
}
public void setTcpNoDelay(boolean on) throws SocketException {}
@Override
public void close() throws IOException {
close(sock);
}
public void shutdownInput() throws IOException {
inputStream.close();
}
public void shutdownOutput() throws IOException {
outputStream.close();
}
@Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
}

View File

@ -50,7 +50,11 @@ public class SocketChannel extends SelectableChannel
}
public Socket socket() {
return new Handle();
try {
return new Handle();
} catch (IOException e) {
return null;
}
}
public boolean connect(SocketAddress address) throws IOException {
@ -165,7 +169,11 @@ public class SocketChannel extends SelectableChannel
}
public class Handle extends Socket {
public void setTcpNoDelay(boolean on) throws SocketException {
public Handle() throws IOException {
super();
}
public void setTcpNoDelay(boolean on) throws SocketException {
natSetTcpNoDelay(socket, on);
}
}

184
classpath/sockets.cpp Normal file
View File

@ -0,0 +1,184 @@
/* Copyright (c) 2008-2013, 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. */
/*
* This file implements a simple cross-platform JNI sockets API
* It is used from different classes of the default Avian classpath
*/
#include "sockets.h"
namespace avian {
namespace classpath {
namespace sockets {
int last_socket_error() {
#ifdef PLATFORM_WINDOWS
int error = WSAGetLastError();
#else
int error = errno;
#endif
return error;
}
void init(JNIEnv* ONLY_ON_WINDOWS(e)) {
#ifdef PLATFORM_WINDOWS
static bool wsaInitialized = false;
if (not wsaInitialized) {
WSADATA data;
int r = WSAStartup(MAKEWORD(2, 2), &data);
if (r or LOBYTE(data.wVersion) != 2 or HIBYTE(data.wVersion) != 2) {
throwNew(e, "java/io/IOException", "WSAStartup failed");
} else {
wsaInitialized = true;
}
}
#endif
}
SOCKET create(JNIEnv* e) {
SOCKET sock;
if (INVALID_SOCKET == (sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) {
char buf[255];
sprintf(buf, "Can't create a socket. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
return 0; // This doesn't matter cause we have risen an exception
}
return sock;
}
void connect(JNIEnv* e, SOCKET sock, long addr, short port) {
sockaddr_in adr;
adr.sin_family = AF_INET;
#ifdef PLATFORM_WINDOWS
adr.sin_addr.S_un.S_addr = htonl(addr);
#else
adr.sin_addr.s_addr = htonl(addr);
#endif
adr.sin_port = htons (port);
if (SOCKET_ERROR == ::connect(sock, (sockaddr* )&adr, sizeof(adr)))
{
char buf[255];
sprintf(buf, "Can't connect a socket. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
return;
}
}
void bind(JNIEnv* e, SOCKET sock, long addr, short port) {
sockaddr_in adr;
adr.sin_family = AF_INET;
#ifdef PLATFORM_WINDOWS
adr.sin_addr.S_un.S_addr = htonl(addr);
#else
adr.sin_addr.s_addr = htonl(addr);
#endif
adr.sin_port = htons (port);
if (SOCKET_ERROR == ::bind(sock, (sockaddr* )&adr, sizeof(adr)))
{
char buf[255];
sprintf(buf, "Can't bind a socket. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
return;
}
}
SOCKET accept(JNIEnv* e, SOCKET sock, long* client_addr, short* client_port) {
sockaddr_in adr;
SOCKET client_socket = ::accept(sock, (sockaddr* )&adr, NULL);
if (INVALID_SOCKET == client_socket) {
char buf[255];
sprintf(buf, "Can't accept the incoming connection. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
return INVALID_SOCKET;
}
if (client_addr != NULL) {
#ifdef PLATFORM_WINDOWS
*client_addr = ntohl(adr.sin_addr.S_un.S_addr);
#else
*client_addr = ntohl(adr.sin_addr.s_addr);
#endif
}
if (client_port != NULL) {
*client_port = ntohs (adr.sin_port);
}
return client_socket;
}
void send(JNIEnv* e, SOCKET sock, const char* buff_ptr, int buff_size) {
if (SOCKET_ERROR == ::send(sock, buff_ptr, buff_size, 0)) {
char buf[255];
sprintf(buf, "Can't send data through the socket. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
return;
}
}
int recv(JNIEnv* e, SOCKET sock, char* buff_ptr, int buff_size) {
int length = ::recv(sock, buff_ptr, buff_size, 0);
if (SOCKET_ERROR == length) {
char buf[255];
sprintf(buf, "Can't receive data through the socket. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
return 0; // This doesn't matter cause we have risen an exception
}
return length;
}
void abort(JNIEnv* e, SOCKET sock) {
if (SOCKET_ERROR == ::closesocket(sock)) {
char buf[255];
sprintf(buf, "Can't close the socket. System error: %d", last_socket_error());
throwNew(e, "java/io/IOException", buf);
}
}
void close(JNIEnv* e, SOCKET sock) {
if (SOCKET_ERROR == ::shutdown(sock, SD_BOTH)) {
int errcode = last_socket_error();
if (errcode != ENOTCONN) {
char buf[255];
sprintf(buf, "Can't shutdown the socket. System error: %d", errcode);
throwNew(e, "java/io/IOException", buf);
}
}
}
void close_input(JNIEnv* e, SOCKET sock) {
if (SOCKET_ERROR == ::shutdown(sock, SD_RECEIVE)) {
int errcode = last_socket_error();
if (errcode != ENOTCONN) {
char buf[255];
sprintf(buf, "Can't shutdown the socket. System error: %d", errcode);
throwNew(e, "java/io/IOException", buf);
}
}
}
void close_output(JNIEnv* e, SOCKET sock) {
if (SOCKET_ERROR == ::shutdown(sock, SD_SEND)) {
int errcode = last_socket_error();
if (errcode != ENOTCONN) {
char buf[255];
sprintf(buf, "Can't shutdown the socket. System error: %d", errcode);
throwNew(e, "java/io/IOException", buf);
}
}
}
}
}
}

72
classpath/sockets.h Normal file
View File

@ -0,0 +1,72 @@
/* Copyright (c) 2008-2013, 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. */
/*
* This file represents a simple cross-platform JNI sockets API
* It is used from different classes of the default Avian classpath
*/
#ifndef SOCKETS_H_
#define SOCKETS_H_
#include "avian/common.h"
#include "jni.h"
#include "jni-util.h"
#ifdef PLATFORM_WINDOWS
# include <winsock2.h>
# define ONLY_ON_WINDOWS(x) x
#else
# include <netdb.h>
# include <sys/socket.h>
# include <netinet/in.h>
# include <unistd.h>
# define ONLY_ON_WINDOWS(x)
# define SOCKET int
# define INVALID_SOCKET -1
# define SOCKET_ERROR -1
# define closesocket(x) close(x)
# define SD_RECEIVE SHUT_RD
# define SD_SEND SHUT_WR
# define SD_BOTH SHUT_RDWR
#endif
namespace avian {
namespace classpath {
namespace sockets {
// Library initialization
void init(JNIEnv* ONLY_ON_WINDOWS(e));
// Socket initialization
SOCKET create(JNIEnv* e);
void connect(JNIEnv* e, SOCKET sock, long addr, short port);
void bind(JNIEnv* e, SOCKET sock, long addr, short port);
SOCKET accept(JNIEnv* e, SOCKET sock, long* client_addr, short* client_port);
// I/O
void send(JNIEnv* e, SOCKET sock, const char* buff_ptr, int buff_size);
int recv(JNIEnv* e, SOCKET sock, char* buff_ptr, int buff_size);
// Socket closing
void abort(JNIEnv* e, SOCKET sock);
void close(JNIEnv* e, SOCKET sock);
void close_input(JNIEnv* e, SOCKET sock);
void close_output(JNIEnv* e, SOCKET sock);
}
}
}
#endif /* SOCKETS_H_ */

37
test/Sockets.java Normal file
View File

@ -0,0 +1,37 @@
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class Sockets {
/**
* @param args
* @throws IOException
* @throws UnknownHostException
*/
public static void main(String[] args) throws UnknownHostException,
IOException {
System.out.print("Requesting...\n");
try (Socket sock = new Socket("www.google.com", 80)) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
String request = "GET /?gws_rd=cr HTTP/1.1\r\n"
+ "Host: www.google.com\r\n" + "Accept: */*\r\n"
+ "User-Agent: Java\r\n" + "Connection: close\r\n" + "\r\n";
bw.write(request);
bw.flush();
BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
String read = null;
while ((read = br.readLine()) != null) {
System.out.println(read);
}
bw.close();
sock.close();
}
}
}