one.nio.net.native.socket.c Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of one-nio Show documentation
Show all versions of one-nio Show documentation
Unconventional Java I/O library
The newest version!
/*
* Copyright 2015-2016 Odnoklassniki Ltd, Mail.Ru Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "jni_util.h"
static int use_IPv6 = 0;
static jfieldID f_fd;
static jfieldID f_cmsgType;
static jfieldID f_cmsgData;
static jclass c_AddressHolder;
static jfieldID f_address;
static jmethodID m_createIPv4Address;
static jmethodID m_createIPv6Address;
static jmethodID m_createUnixAddress;
static jthrowable t_SocketClosedException;
static pthread_t* fd_table;
// sys/un.h does not have it by either reason
#define UNIX_PATH_MAX 108
#ifndef SO_REUSEPORT
#define SO_REUSEPORT 15
#endif
#ifndef TCP_FASTOPEN
#define TCP_FASTOPEN 23
#endif
#ifndef TCP_NOTSENT_LOWAT
#define TCP_NOTSENT_LOWAT 25
#endif
static socklen_t sockaddr_from_java(JNIEnv* env, jobject address, jint port, struct sockaddr_storage* sa) {
// AF_UNIX
if (port < 0) {
struct sockaddr_un* sun = (struct sockaddr_un*)sa;
sun->sun_family = AF_UNIX;
const char* path = (*env)->GetStringUTFChars(env, (jstring)address, NULL);
if (path != NULL) {
strncpy(sun->sun_path, path, UNIX_PATH_MAX);
(*env)->ReleaseStringUTFChars(env, (jstring)address, path);
}
return sizeof(struct sockaddr_un);
}
// AF_INET / AF_INET6
jbyteArray array = (jbyteArray)address;
if (use_IPv6) {
struct sockaddr_in6* sin = (struct sockaddr_in6*)sa;
sin->sin6_family = AF_INET6;
sin->sin6_port = htons(port);
sin->sin6_flowinfo = 0;
sin->sin6_scope_id = 0;
if ((*env)->GetArrayLength(env, array) == 4) {
((int*)&sin->sin6_addr)[0] = 0;
((int*)&sin->sin6_addr)[1] = 0;
((int*)&sin->sin6_addr)[2] = htonl(0xffff);
(*env)->GetByteArrayRegion(env, array, 0, 4, (jbyte*)&sin->sin6_addr + 12);
} else {
(*env)->GetByteArrayRegion(env, array, 0, 16, (jbyte*)&sin->sin6_addr);
}
return sizeof(struct sockaddr_in6);
} else {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
sin->sin_family = AF_INET;
sin->sin_port = htons(port);
(*env)->GetByteArrayRegion(env, array, 0, 4, (jbyte*)&sin->sin_addr);
return sizeof(struct sockaddr_in);
}
}
jobject sockaddr_to_java(JNIEnv* env, struct sockaddr_storage* sa, socklen_t len) {
if (sa->ss_family == AF_INET) {
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
int ip = ntohl(sin->sin_addr.s_addr);
int port = ntohs(sin->sin_port);
return (*env)->CallStaticObjectMethod(env, c_AddressHolder, m_createIPv4Address, ip, port);
}
if (sa->ss_family == AF_INET6) {
struct sockaddr_in6* sin = (struct sockaddr_in6*)sa;
const int* a = (const int*)&sin->sin6_addr;
int port = ntohs(sin->sin6_port);
if (a[0] == 0 && a[1] == 0 && a[2] == htonl(0xffff)) {
// IPv4-mapped address
return (*env)->CallStaticObjectMethod(env, c_AddressHolder, m_createIPv4Address, ntohl(a[3]), port);
}
return (*env)->CallStaticObjectMethod(env, c_AddressHolder, m_createIPv6Address,
ntohl(a[0]), ntohl(a[1]), ntohl(a[2]), ntohl(a[3]), port);
}
if (sa->ss_family == AF_UNIX && len > sizeof(sa_family_t)) {
struct sockaddr_un* sun = (struct sockaddr_un*)sa;
sun->sun_path[UNIX_PATH_MAX - 1] = 0;
jstring path = (*env)->NewStringUTF(env, sun->sun_path);
return (*env)->CallStaticObjectMethod(env, c_AddressHolder, m_createUnixAddress, path);
}
// Unknown address family
return NULL;
}
static int is_udp_socket(int fd) {
int type = 0;
socklen_t length = sizeof(type);
return getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &length) == 0 && type == SOCK_DGRAM;
}
static jint get_int_socket_opt(int fd, int level, int optname) {
int optval;
socklen_t length = sizeof(optval);
if (getsockopt(fd, level, optname, &optval, &length) == 0) {
return optval;
}
return 0;
}
static jboolean get_bool_socket_opt(int fd, int level, int optname) {
return get_int_socket_opt(fd, level, optname) ? JNI_TRUE : JNI_FALSE;
}
static int accept_with_flags(int fd, jboolean nonblock) {
#if defined(__NR_accept4) && defined(SOCK_NONBLOCK)
return syscall(__NR_accept4, fd, NULL, NULL, nonblock ? SOCK_NONBLOCK : 0);
#else
int result = accept(fd, NULL, NULL);
if (result != -1 && nonblock) {
fcntl(result, F_SETFL, O_NONBLOCK);
}
return result;
#endif
}
static void wakeup_handler(int sig) {
// Empty handler
}
static inline pthread_t* start_blocking_call(int fd) {
pthread_t* fd_lock = &fd_table[fd];
*fd_lock = pthread_self();
return fd_lock;
}
static inline void end_blocking_call(pthread_t* fd_lock) {
*fd_lock = 0;
}
static inline void wakeup_blocking_call(int fd) {
pthread_t blocked_thread = __sync_lock_test_and_set(&fd_table[fd], 0);
if (blocked_thread != 0) {
pthread_kill(blocked_thread, SIG_WAKEUP);
}
}
// Avoid creating a new exception object and getting a stack trace on a fast path
void throw_socket_closed_cached(JNIEnv* env) {
(*env)->Throw(env, t_SocketClosedException);
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_initNatives(JNIEnv* env, jclass cls, jboolean preferIPv4) {
struct rlimit max_files;
struct sigaction sa;
// Check IPv6 support
if (!preferIPv4) {
int s = socket(AF_INET6, SOCK_STREAM, 0);
if (s != -1) {
use_IPv6 = 1;
close(s);
}
}
// Cache field ID to access NativeSocket.fd
f_fd = cache_field(env, "one/nio/net/NativeSocket", "fd", "I");
// Cache Msg fields
f_cmsgType = cache_field(env, "one/nio/net/Msg", "cmsgType", "I");
f_cmsgData = cache_field(env, "one/nio/net/Msg", "cmsgData", "[I");
// Cache method IDs to produce InetSocketAddress
c_AddressHolder = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "one/nio/net/AddressHolder"));
f_address = (*env)->GetFieldID(env, c_AddressHolder, "address", "Ljava/net/InetSocketAddress;");
m_createIPv4Address = (*env)->GetStaticMethodID(env, c_AddressHolder, "createIPv4Address", "(II)Ljava/net/InetSocketAddress;");
m_createIPv6Address = (*env)->GetStaticMethodID(env, c_AddressHolder, "createIPv6Address", "(IIIII)Ljava/net/InetSocketAddress;");
m_createUnixAddress = (*env)->GetStaticMethodID(env, c_AddressHolder, "createUnixAddress", "(Ljava/lang/String;)Ljava/net/InetSocketAddress;");
// Create a shared SocketClosedException for fast throw
jclass SocketClosedException = (*env)->FindClass(env, "one/nio/net/SocketClosedException");
jmethodID init = (*env)->GetMethodID(env, SocketClosedException, "", "()V");
t_SocketClosedException = (*env)->NewGlobalRef(env, (*env)->NewObject(env, SocketClosedException, init));
// Allocate table for thread pointer per file descriptor
getrlimit(RLIMIT_NOFILE, &max_files);
fd_table = (pthread_t*) calloc(max_files.rlim_max, sizeof(pthread_t));
// Setup dummy signal handler for SIG_WAKEUP, the signal is used to interrupt blocking I/O
sa.sa_handler = wakeup_handler;
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sigaction(SIG_WAKEUP, &sa, NULL);
return use_IPv6 ? AF_INET6 : AF_INET;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_socket0(JNIEnv* env, jclass cls, jint domain, jint type) {
int result = socket(domain, type, 0);
if (result == -1) {
throw_io_exception(env);
}
return result;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_accept0(JNIEnv* env, jobject self, jboolean nonblock) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
return -1;
} else {
pthread_t* fd_lock = start_blocking_call(fd);
int result = accept_with_flags(fd, nonblock);
end_blocking_call(fd_lock);
if (result == -1) {
if (errno == EWOULDBLOCK) {
return -1;
}
throw_io_exception(env);
}
return result;
}
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_close(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd != -1) {
(*env)->SetIntField(env, self, f_fd, -1);
shutdown(fd, SHUT_RDWR);
wakeup_blocking_call(fd);
close(fd);
}
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_connect0(JNIEnv* env, jobject self, jobject address, jint port) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else {
struct sockaddr_storage sa;
socklen_t len = sockaddr_from_java(env, address, port, &sa);
while (connect(fd, (struct sockaddr*)&sa, len) != 0) {
if (errno != EINTR) {
throw_io_exception(env);
break;
}
}
}
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_bind0(JNIEnv* env, jobject self, jobject address, jint port) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else {
struct sockaddr_storage sa;
socklen_t len = sockaddr_from_java(env, address, port, &sa);
if (bind(fd, (struct sockaddr*)&sa, len) != 0) {
throw_io_exception(env);
}
}
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_listen(JNIEnv* env, jobject self, jint backlog) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else if (listen(fd, backlog) != 0) {
throw_io_exception(env);
}
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_writeRaw(JNIEnv* env, jobject self, jlong buf, jint count, jint flags) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
do {
int result = send(fd, (void*)(intptr_t)buf, count, flags | MSG_NOSIGNAL);
if (result > 0) {
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT int JNICALL
Java_one_nio_net_NativeSocket_write(JNIEnv* env, jobject self, jbyteArray data, jint offset, jint count, jint flags) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[MAX_STACK_BUF];
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
if (count > MAX_STACK_BUF) count = MAX_STACK_BUF;
(*env)->GetByteArrayRegion(env, data, offset, count, buf);
do {
int result = send(fd, buf, count, flags | MSG_NOSIGNAL);
if (result > 0) {
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_writeFully(JNIEnv* env, jobject self, jbyteArray data, jint offset, jint count) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[MAX_STACK_BUF];
if (fd == -1) {
throw_socket_closed(env);
} else {
while (count > 0) {
int to_write = count <= MAX_STACK_BUF ? count : MAX_STACK_BUF;
(*env)->GetByteArrayRegion(env, data, offset, to_write, buf);
int result = send(fd, buf, to_write, MSG_NOSIGNAL);
if (result > 0) {
offset += result;
count -= result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
}
}
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_readRaw(JNIEnv* env, jobject self, jlong buf, jint count, jint flags) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
do {
int result = recv(fd, (void*)(intptr_t)buf, count, flags);
if (result > 0) {
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT int JNICALL
Java_one_nio_net_NativeSocket_read(JNIEnv* env, jobject self, jbyteArray data, jint offset, jint count, jint flags) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[MAX_STACK_BUF];
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
do {
int result = recv(fd, buf, count <= MAX_STACK_BUF ? count : MAX_STACK_BUF, flags);
if (result > 0) {
(*env)->SetByteArrayRegion(env, data, offset, result, buf);
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_readFully(JNIEnv* env, jobject self, jbyteArray data, jint offset, jint count) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[MAX_STACK_BUF];
if (fd == -1) {
throw_socket_closed(env);
} else {
while (count > 0) {
int result = recv(fd, buf, count <= MAX_STACK_BUF ? count : MAX_STACK_BUF, 0);
if (result > 0) {
(*env)->SetByteArrayRegion(env, data, offset, result, buf);
offset += result;
count -= result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
}
}
}
JNIEXPORT jlong JNICALL
Java_one_nio_net_NativeSocket_sendFile0(JNIEnv* env, jobject self, jint sourceFD, jlong offset, jlong count) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
do {
jlong result = sendfile(fd, sourceFD, (off_t*)&offset, count);
if (result > 0) {
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_sendTo0(JNIEnv* env, jobject self, jbyteArray data, jint offset, jint count,
jint flags, jobject address, jint port) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[MAX_STACK_BUF];
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
if (count > MAX_STACK_BUF) count = MAX_STACK_BUF;
(*env)->GetByteArrayRegion(env, data, offset, count, buf);
struct sockaddr_storage sa;
socklen_t len = sockaddr_from_java(env, address, port, &sa);
do {
int result = sendto(fd, (void*)(intptr_t)buf, count, flags | MSG_NOSIGNAL, (struct sockaddr*)&sa, len);
if (result > 0) {
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_sendTo1(JNIEnv* env, jobject self, jlong buffer, jint count,
jint flags, jobject address, jint port) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
struct sockaddr_storage sa;
socklen_t len = sockaddr_from_java(env, address, port, &sa);
do {
int result = sendto(fd, (void*)(intptr_t)buffer, count, flags | MSG_NOSIGNAL, (struct sockaddr*)&sa, len);
if (result > 0) {
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_recvFrom0(JNIEnv* env, jobject self, jbyteArray data, jint offset, jint count,
jint flags, jobject holder) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[MAX_STACK_BUF];
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
do {
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);
int result = recvfrom(fd, (void*)(intptr_t)buf, count <= MAX_STACK_BUF ? count : MAX_STACK_BUF,
flags, (struct sockaddr*)&sa, &len);
if (result > 0 || (result == 0 && is_udp_socket(fd))) {
(*env)->SetByteArrayRegion(env, data, offset, result, buf);
(*env)->SetObjectField(env, holder, f_address, sockaddr_to_java(env, &sa, len));
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_recvFrom1(JNIEnv* env, jobject self, jlong buffer, jint count,
jint flags, jobject holder) {
int fd = (*env)->GetIntField(env, self, f_fd);
if (fd == -1) {
throw_socket_closed(env);
} else if (count != 0) {
do {
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);
int result = recvfrom(fd, (void*)(intptr_t)buffer, count, flags, (struct sockaddr*)&sa, &len);
if (result > 0 || (result == 0 && is_udp_socket(fd))) {
(*env)->SetObjectField(env, holder, f_address, sockaddr_to_java(env, &sa, len));
return result;
} else if (result == 0) {
throw_socket_closed_cached(env);
break;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_sendMsg0(JNIEnv* env, jobject self, jbyteArray data,
jint cmsgType, jintArray cmsgData, jint flags) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte data_buf[MAX_STACK_BUF - 4096];
struct {
struct cmsghdr hdr;
jint data[1000];
} cmsg_buf;
if (fd == -1) {
throw_socket_closed(env);
} else {
struct msghdr msg = {0};
struct iovec iov;
if (data != NULL) {
jint data_len = (*env)->GetArrayLength(env, data);
if (data_len > sizeof(data_buf)) {
data_len = sizeof(data_buf);
}
(*env)->GetByteArrayRegion(env, data, 0, data_len, data_buf);
iov.iov_base = data_buf;
iov.iov_len = data_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
}
if (cmsgData != NULL) {
jint cmsgLen = (*env)->GetArrayLength(env, cmsgData);
if (cmsgLen > sizeof(cmsg_buf.data) / sizeof(jint)) {
cmsgLen = sizeof(cmsg_buf.data) / sizeof(jint);
}
(*env)->GetIntArrayRegion(env, cmsgData, 0, cmsgLen, cmsg_buf.data);
cmsg_buf.hdr.cmsg_level = SOL_SOCKET;
cmsg_buf.hdr.cmsg_type = cmsgType;
cmsg_buf.hdr.cmsg_len = CMSG_LEN(cmsgLen * sizeof(jint));
msg.msg_control = &cmsg_buf;
msg.msg_controllen = CMSG_SPACE(cmsgLen * sizeof(jint));
}
do {
ssize_t result = sendmsg(fd, &msg, flags);
if (result >= 0) {
return result;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_recvMsg0(JNIEnv* env, jobject self, jbyteArray data,
jobject jmsg, jint flags) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte data_buf[MAX_STACK_BUF - 4096];
struct {
struct cmsghdr hdr;
jint data[1000];
} cmsg_buf;
if (fd == -1) {
throw_socket_closed(env);
} else {
struct msghdr msg = {0};
struct iovec iov;
if (data != NULL) {
int data_len = (*env)->GetArrayLength(env, data);
if (data_len > sizeof(data_buf)) {
data_len = sizeof(data_buf);
}
iov.iov_base = data_buf;
iov.iov_len = data_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
}
if (jmsg != NULL) {
msg.msg_control = &cmsg_buf;
msg.msg_controllen = sizeof(cmsg_buf);
}
do {
ssize_t result = recvmsg(fd, &msg, flags);
if (result >= 0) {
if (data != NULL) {
(*env)->SetByteArrayRegion(env, data, 0, result, data_buf);
}
struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg);
if (jmsg != NULL && hdr != NULL && hdr->cmsg_level == SOL_SOCKET) {
jint cmsgLen = (hdr->cmsg_len - CMSG_LEN(0)) / sizeof(jint);
jintArray cmsgData = (*env)->NewIntArray(env, cmsgLen);
if (cmsgData != NULL) {
(*env)->SetIntArrayRegion(env, cmsgData, 0, cmsgLen, cmsg_buf.data);
(*env)->SetIntField(env, jmsg, f_cmsgType, hdr->cmsg_type);
(*env)->SetObjectField(env, jmsg, f_cmsgData, cmsgData);
}
}
return result;
} else if (is_io_exception(fd)) {
throw_io_exception(env);
break;
}
} while (errno == EINTR);
}
return 0;
}
JNIEXPORT jobject JNICALL
Java_one_nio_net_NativeSocket_getLocalAddress(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);
if (getsockname(fd, (struct sockaddr*)&sa, &len) != 0) {
return NULL;
}
return sockaddr_to_java(env, &sa, len);
}
JNIEXPORT jobject JNICALL
Java_one_nio_net_NativeSocket_getRemoteAddress(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);
if (getpeername(fd, (struct sockaddr*)&sa, &len) != 0) {
return NULL;
}
return sockaddr_to_java(env, &sa, len);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setBlocking(JNIEnv* env, jobject self, jboolean blocking) {
int fd = (*env)->GetIntField(env, self, f_fd);
fcntl(fd, F_SETFL, blocking ? 0 : O_NONBLOCK);
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_isBlocking(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return (fcntl(fd, F_GETFL) & O_NONBLOCK) != O_NONBLOCK;
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setTimeout(JNIEnv* env, jobject self, jint timeout) {
int fd = (*env)->GetIntField(env, self, f_fd);
struct timeval tv;
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_getTimeout(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
struct timeval tv;
socklen_t len = sizeof(tv);
if (getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, &len) == 0) {
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
return 0;
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setKeepAlive(JNIEnv* env, jobject self, jboolean keepAlive) {
int fd = (*env)->GetIntField(env, self, f_fd);
int value = (int) keepAlive;
setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value));
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getKeepAlive(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_SOCKET, SO_KEEPALIVE);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setNoDelay(JNIEnv* env, jobject self, jboolean noDelay) {
int fd = (*env)->GetIntField(env, self, f_fd);
int value = (int) noDelay;
setsockopt(fd, SOL_TCP, TCP_NODELAY, &value, sizeof(value));
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getNoDelay(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_TCP, TCP_NODELAY);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setTcpFastOpen(JNIEnv* env, jobject self, jboolean tcpFastOpen) {
int fd = (*env)->GetIntField(env, self, f_fd);
int value = tcpFastOpen ? 128 : 0;
setsockopt(fd, SOL_TCP, TCP_FASTOPEN, &value, sizeof(value));
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getTcpFastOpen(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_TCP, TCP_FASTOPEN);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setDeferAccept(JNIEnv* env, jobject self, jboolean deferAccept) {
int fd = (*env)->GetIntField(env, self, f_fd);
int value = (int) deferAccept;
setsockopt(fd, SOL_TCP, TCP_DEFER_ACCEPT, &value, sizeof(value));
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getDeferAccept(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_TCP, TCP_DEFER_ACCEPT);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setReuseAddr(JNIEnv* env, jobject self, jboolean reuseAddr, jboolean reusePort) {
int fd = (*env)->GetIntField(env, self, f_fd);
int value = (int) reuseAddr;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value));
value = (int) reusePort;
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value));
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getReuseAddr(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_SOCKET, SO_REUSEADDR);
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getReusePort(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_SOCKET, SO_REUSEPORT);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setRecvBuffer(JNIEnv* env, jobject self, jint recvBuf) {
int fd = (*env)->GetIntField(env, self, f_fd);
setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &recvBuf, sizeof(recvBuf));
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_getRecvBuffer(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_int_socket_opt(fd, SOL_SOCKET, SO_RCVBUF) / 2;
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setSendBuffer(JNIEnv* env, jobject self, jint sendBuf) {
int fd = (*env)->GetIntField(env, self, f_fd);
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sendBuf, sizeof(sendBuf));
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_getSendBuffer(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_int_socket_opt(fd, SOL_SOCKET, SO_SNDBUF) / 2;
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setTos(JNIEnv* env, jobject self, jint tos) {
int fd = (*env)->GetIntField(env, self, f_fd);
setsockopt(fd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_getTos(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_int_socket_opt(fd, IPPROTO_IP, IP_TOS);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setNotsentLowat(JNIEnv* env, jobject self, jint lowat) {
int fd = (*env)->GetIntField(env, self, f_fd);
setsockopt(fd, SOL_TCP, TCP_NOTSENT_LOWAT, &lowat, sizeof(lowat));
}
JNIEXPORT jint JNICALL
Java_one_nio_net_NativeSocket_getNotsentLowat(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_int_socket_opt(fd, SOL_TCP, TCP_NOTSENT_LOWAT);
}
JNIEXPORT void JNICALL
Java_one_nio_net_NativeSocket_setThinLinearTimeouts(JNIEnv* env, jobject self, jboolean thinLto) {
int fd = (*env)->GetIntField(env, self, f_fd);
int value = (int) thinLto;
setsockopt(fd, SOL_TCP, TCP_THIN_LINEAR_TIMEOUTS, &value, sizeof(value));
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_getThinLinearTimeouts(JNIEnv* env, jobject self) {
int fd = (*env)->GetIntField(env, self, f_fd);
return get_bool_socket_opt(fd, SOL_TCP, TCP_THIN_LINEAR_TIMEOUTS);
}
JNIEXPORT jbyteArray JNICALL
Java_one_nio_net_NativeSocket_getOption(JNIEnv* env, jobject self, jint level, jint option) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[1024];
socklen_t len = sizeof(buf);
if (getsockopt(fd, level, option, buf, &len) == 0) {
jbyteArray result = (*env)->NewByteArray(env, len);
if (result != NULL) (*env)->SetByteArrayRegion(env, result, 0, len, buf);
return result;
}
return NULL;
}
JNIEXPORT jboolean JNICALL
Java_one_nio_net_NativeSocket_setOption(JNIEnv* env, jobject self, jint level, jint option, jbyteArray value) {
int fd = (*env)->GetIntField(env, self, f_fd);
jbyte buf[1024];
socklen_t len = (*env)->GetArrayLength(env, value);
(*env)->GetByteArrayRegion(env, value, 0, len, buf);
return setsockopt(fd, level, option, buf, len) == 0 ? JNI_TRUE : JNI_FALSE;
}