Decouple Unix from Linux in Native Transport
Motivation: transport-native-epoll is designed to be specific to Linux. However there is native code that can be extracted out and made to work on more Unix like distributions. There are a few steps to be completely decoupled but the first step is to extract out code that can run in a more general Unix environment from the Linux specific code base. Modifications: - Move all non-Linux specific stuff from Native.java into the io.netty.channel.unix package. - io.netty.channel.unix.FileDescriptor will inherit all the native methods that are specific to file descriptors. - io_netty_channel_epoll_Native.[c|h] will only have code that is specific to Linux. Result: Code is decoupled and design is streamlined in FileDescriptor.
This commit is contained in:
parent
19658e9cd8
commit
dbbdbe11a6
File diff suppressed because it is too large
Load Diff
@ -41,48 +41,16 @@ jint Java_io_netty_channel_epoll_Native_epollWait0(JNIEnv* env, jclass clazz, ji
|
||||
jint Java_io_netty_channel_epoll_Native_epollCtlAdd0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags);
|
||||
jint Java_io_netty_channel_epoll_Native_epollCtlMod0(JNIEnv* env, jclass clazz, jint efd, jint fd, jint flags);
|
||||
jint Java_io_netty_channel_epoll_Native_epollCtlDel0(JNIEnv* env, jclass clazz, jint efd, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_write0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
|
||||
jint Java_io_netty_channel_epoll_Native_writeAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
|
||||
jlong Java_io_netty_channel_epoll_Native_writev0(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length);
|
||||
jlong Java_io_netty_channel_epoll_Native_writevAddresses0(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length);
|
||||
jint Java_io_netty_channel_epoll_Native_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_epoll_Native_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_epoll_Native_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_sendmmsg(JNIEnv* env, jclass clazz, jint fd, jobjectArray packets, jint offset, jint len);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_read0(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
|
||||
jint Java_io_netty_channel_epoll_Native_readAddress0(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
|
||||
jobject Java_io_netty_channel_epoll_Native_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
|
||||
jobject Java_io_netty_channel_epoll_Native_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
|
||||
jint Java_io_netty_channel_epoll_Native_close0(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_shutdown0(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write);
|
||||
jint Java_io_netty_channel_epoll_Native_socketStream(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_socketDgram(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_socketDomain(JNIEnv* env, jclass clazz);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_epoll_Native_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
|
||||
jint Java_io_netty_channel_epoll_Native_recvFd0(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_sendFd0(JNIEnv* env, jclass clazz, jint socketFd, jint fd);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_listen0(JNIEnv* env, jclass clazz, jint fd, jint backlog);
|
||||
jint Java_io_netty_channel_epoll_Native_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_epoll_Native_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
|
||||
jint Java_io_netty_channel_epoll_Native_finishConnect0(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_accept0(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress);
|
||||
jlong Java_io_netty_channel_epoll_Native_sendfile0(JNIEnv* env, jclass clazz, jint fd, jobject fileRegion, jlong base_off, jlong off, jlong len);
|
||||
jbyteArray Java_io_netty_channel_epoll_Native_remoteAddress0(JNIEnv* env, jclass clazz, jint fd);
|
||||
jbyteArray Java_io_netty_channel_epoll_Native_localAddress0(JNIEnv* env, jclass clazz, jint fd);
|
||||
|
||||
void Java_io_netty_channel_epoll_Native_setReuseAddress(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setReusePort(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setTcpFastopen(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setTrafficClass(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setBroadcast(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_epoll_Native_setTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
@ -92,19 +60,14 @@ void Java_io_netty_channel_epoll_Native_setIpFreeBind(JNIEnv* env, jclass clazz,
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_isReuseAddresss(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_isReusePort(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_isTcpCork(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getTcpNotSentLowAt(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getSoLinger(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getTrafficClass(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_isBroadcast(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getTcpKeepIdle(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getTcpKeepIntvl(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getTcpKeepCnt(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_getSoError(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_epoll_Native_isIpFreeBind(JNIEnv* env, jclass clazz, jint fd);
|
||||
void Java_io_netty_channel_epoll_Native_tcpInfo0(JNIEnv* env, jclass clazz, jint fd, jintArray array);
|
||||
|
||||
jstring Java_io_netty_channel_epoll_Native_kernelVersion(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_iovMax(JNIEnv* env, jclass clazz);
|
||||
@ -113,15 +76,6 @@ jlong Java_io_netty_channel_epoll_Native_ssizeMax(JNIEnv* env, jclass clazz);
|
||||
jboolean Java_io_netty_channel_epoll_Native_isSupportingSendmmsg(JNIEnv* env, jclass clazz);
|
||||
jboolean Java_io_netty_channel_epoll_Native_isSupportingTcpFastopen(JNIEnv* env, jclass clazz);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_errnoENOTCONN(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_errnoEBADF(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_errnoEPIPE(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_errnoECONNRESET(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_errnoEAGAIN(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_errnoEINPROGRESS(JNIEnv* env, jclass clazz);
|
||||
jstring Java_io_netty_channel_epoll_Native_strError(JNIEnv* env, jclass clazz, jint err);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_epollin(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_epollout(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_epollrdhup(JNIEnv* env, jclass clazz);
|
||||
@ -130,7 +84,6 @@ jint Java_io_netty_channel_epoll_Native_epollerr(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_sizeofEpollEvent(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_offsetofEpollData(JNIEnv* env, jclass clazz);
|
||||
|
||||
jlong Java_io_netty_channel_epoll_Native_pipe0(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_epoll_Native_splice0(JNIEnv* env, jclass clazz, jint fd, jlong offIn, jint fdOut, jlong offOut, jlong len);
|
||||
|
||||
jint Java_io_netty_channel_epoll_Native_tcpMd5SigMaxKeyLen(JNIEnv* env, jclass clazz);
|
||||
|
191
transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.c
Normal file
191
transport-native-epoll/src/main/c/io_netty_channel_unix_Errors.c
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "netty_unix_errors.h"
|
||||
#include "io_netty_channel_unix_Errors.h"
|
||||
|
||||
static jclass runtimeExceptionClass = NULL;
|
||||
static jclass channelExceptionClass = NULL;
|
||||
static jclass ioExceptionClass = NULL;
|
||||
static jclass closedChannelExceptionClass = NULL;
|
||||
static jmethodID closedChannelExceptionMethodId = NULL;
|
||||
|
||||
/** Notice: every usage of exceptionMessage needs to release the allocated memory for the sequence of char */
|
||||
static char* exceptionMessage(char* msg, int error) {
|
||||
if (error < 0) {
|
||||
// some functions return negative values
|
||||
// and it's hard to keep track of when to send -error and when not
|
||||
// this will just take care when things are forgotten
|
||||
// what would generate a proper error
|
||||
error = error * -1;
|
||||
}
|
||||
//strerror is returning a constant, so no need to free anything coming from strerror
|
||||
char* err = strerror(error);
|
||||
char* result = malloc(strlen(msg) + strlen(err) + 1);
|
||||
strcpy(result, msg);
|
||||
strcat(result, err);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Exported C methods
|
||||
void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message) {
|
||||
(*env)->ThrowNew(env, runtimeExceptionClass, message);
|
||||
}
|
||||
|
||||
void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
|
||||
char* allocatedMessage = exceptionMessage(message, errorNumber);
|
||||
(*env)->ThrowNew(env, runtimeExceptionClass, allocatedMessage);
|
||||
free(allocatedMessage);
|
||||
}
|
||||
|
||||
void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
|
||||
char* allocatedMessage = exceptionMessage(message, errorNumber);
|
||||
(*env)->ThrowNew(env, channelExceptionClass, allocatedMessage);
|
||||
free(allocatedMessage);
|
||||
}
|
||||
|
||||
void netty_unix_errors_throwIOException(JNIEnv* env, char* message) {
|
||||
(*env)->ThrowNew(env, ioExceptionClass, message);
|
||||
}
|
||||
|
||||
void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber) {
|
||||
char* allocatedMessage = exceptionMessage(message, errorNumber);
|
||||
(*env)->ThrowNew(env, ioExceptionClass, allocatedMessage);
|
||||
free(allocatedMessage);
|
||||
}
|
||||
|
||||
void netty_unix_errors_throwClosedChannelException(JNIEnv* env) {
|
||||
jobject exception = (*env)->NewObject(env, closedChannelExceptionClass, closedChannelExceptionMethodId);
|
||||
(*env)->Throw(env, exception);
|
||||
}
|
||||
|
||||
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env) {
|
||||
jclass exceptionClass = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
|
||||
(*env)->ThrowNew(env, exceptionClass, "");
|
||||
}
|
||||
|
||||
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env) {
|
||||
jclass localRuntimeExceptionClass = (*env)->FindClass(env, "java/lang/RuntimeException");
|
||||
if (localRuntimeExceptionClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
runtimeExceptionClass = (jclass) (*env)->NewGlobalRef(env, localRuntimeExceptionClass);
|
||||
if (runtimeExceptionClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
jclass localChannelExceptionClass = (*env)->FindClass(env, "io/netty/channel/ChannelException");
|
||||
if (localChannelExceptionClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
channelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localChannelExceptionClass);
|
||||
if (channelExceptionClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
// cache classes that are used within other jni methods for performance reasons
|
||||
jclass localClosedChannelExceptionClass = (*env)->FindClass(env, "java/nio/channels/ClosedChannelException");
|
||||
if (localClosedChannelExceptionClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
closedChannelExceptionClass = (jclass) (*env)->NewGlobalRef(env, localClosedChannelExceptionClass);
|
||||
if (closedChannelExceptionClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
closedChannelExceptionMethodId = (*env)->GetMethodID(env, closedChannelExceptionClass, "<init>", "()V");
|
||||
if (closedChannelExceptionMethodId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ClosedChannelException.<init>()");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
jclass localIoExceptionClass = (*env)->FindClass(env, "java/io/IOException");
|
||||
if (localIoExceptionClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
ioExceptionClass = (jclass) (*env)->NewGlobalRef(env, localIoExceptionClass);
|
||||
if (ioExceptionClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env) {
|
||||
// delete global references so the GC can collect them
|
||||
if (runtimeExceptionClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, runtimeExceptionClass);
|
||||
runtimeExceptionClass = NULL;
|
||||
}
|
||||
if (channelExceptionClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, channelExceptionClass);
|
||||
channelExceptionClass = NULL;
|
||||
}
|
||||
if (ioExceptionClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, ioExceptionClass);
|
||||
ioExceptionClass = NULL;
|
||||
}
|
||||
if (closedChannelExceptionClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, closedChannelExceptionClass);
|
||||
closedChannelExceptionClass = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Exported JNI Methods
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz) {
|
||||
return ENOTCONN;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz) {
|
||||
return EBADF;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz) {
|
||||
return EPIPE;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz) {
|
||||
return ECONNRESET;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz) {
|
||||
return EAGAIN;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz) {
|
||||
return EWOULDBLOCK;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz) {
|
||||
return EINPROGRESS;
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error) {
|
||||
return (*env)->NewStringUTF(env, strerror(error));
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
#ifndef IO_NETTY_ERROR_UNIX_H_
|
||||
#define IO_NETTY_ERROR_UNIX_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
// Exported JNI methods
|
||||
jint Java_io_netty_channel_unix_Errors_errnoENOTCONN(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Errors_errnoEBADF(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Errors_errnoEPIPE(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Errors_errnoECONNRESET(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Errors_errnoEAGAIN(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Errors_errnoEWOULDBLOCK(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Errors_errnoEINPROGRESS(JNIEnv* env, jclass clazz);
|
||||
jstring Java_io_netty_channel_unix_Errors_strError(JNIEnv* env, jclass clazz, jint error);
|
||||
|
||||
#endif /* IO_NETTY_ERROR_UNIX_H_ */
|
@ -13,12 +13,126 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#include <jni.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_filedescriptor.h"
|
||||
#include "io_netty_channel_unix_FileDescriptor.h"
|
||||
|
||||
static jmethodID posId = NULL;
|
||||
static jmethodID limitId = NULL;
|
||||
static jfieldID posFieldId = NULL;
|
||||
static jfieldID limitFieldId = NULL;
|
||||
|
||||
// Optional external methods
|
||||
extern int pipe2(int pipefd[2], int flags) __attribute__((weak));
|
||||
|
||||
static jint _write(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) {
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = write(fd, buffer + pos, (size_t) (limit - pos));
|
||||
// keep on writing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static jlong _writev(JNIEnv* env, jclass clazz, jint fd, struct iovec* iov, jint length) {
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = writev(fd, iov, length);
|
||||
// keep on writing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jlong) res;
|
||||
}
|
||||
|
||||
static jint _read(JNIEnv* env, jclass clazz, jint fd, void* buffer, jint pos, jint limit) {
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = read(fd, buffer + pos, (size_t) (limit - pos));
|
||||
// Keep on reading if we was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env) {
|
||||
void* mem = malloc(1);
|
||||
if (mem == NULL) {
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
|
||||
if (directBuffer == NULL) {
|
||||
free(mem);
|
||||
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
|
||||
free(mem);
|
||||
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
jclass cls = (*env)->GetObjectClass(env, directBuffer);
|
||||
|
||||
// Get the method id for Buffer.position() and Buffer.limit(). These are used as fallback if
|
||||
// it is not possible to obtain the position and limit using the fields directly.
|
||||
posId = (*env)->GetMethodID(env, cls, "position", "()I");
|
||||
if (posId == NULL) {
|
||||
free(mem);
|
||||
|
||||
// position method was not found.. something is wrong so bail out
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.position()");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
limitId = (*env)->GetMethodID(env, cls, "limit", "()I");
|
||||
if (limitId == NULL) {
|
||||
free(mem);
|
||||
|
||||
// limit method was not found.. something is wrong so bail out
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: ByteBuffer.limit()");
|
||||
return JNI_ERR;
|
||||
}
|
||||
// Try to get the ids of the position and limit fields. We later then check if we was able
|
||||
// to find them and if so use them get the position and limit of the buffer. This is
|
||||
// much faster then call back into java via (*env)->CallIntMethod(...).
|
||||
posFieldId = (*env)->GetFieldID(env, cls, "position", "I");
|
||||
if (posFieldId == NULL) {
|
||||
// this is ok as we can still use the method so just clear the exception
|
||||
(*env)->ExceptionClear(env);
|
||||
}
|
||||
limitFieldId = (*env)->GetFieldID(env, cls, "limit", "I");
|
||||
if (limitFieldId == NULL) {
|
||||
// this is ok as we can still use the method so just clear the exception
|
||||
(*env)->ExceptionClear(env);
|
||||
}
|
||||
|
||||
free(mem);
|
||||
}
|
||||
|
||||
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env) { }
|
||||
|
||||
JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd) {
|
||||
if (close(fd) < 0) {
|
||||
return -errno;
|
||||
@ -27,7 +141,6 @@ JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* en
|
||||
}
|
||||
|
||||
JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path) {
|
||||
|
||||
const char* f_path = (*env)->GetStringUTFChars(env, path, 0);
|
||||
|
||||
int res = open(f_path, O_WRONLY | O_CREAT | O_TRUNC, 0666);
|
||||
@ -38,3 +151,95 @@ JNIEXPORT int JNICALL Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
|
||||
// We check that GetDirectBufferAddress will not return NULL in OnLoad
|
||||
return _write(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
|
||||
return _write(env, clazz, fd, (void*) address, pos, limit);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length) {
|
||||
struct iovec* iov = (struct iovec*) memoryAddress;
|
||||
return _writev(env, clazz, fd, iov, length);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length) {
|
||||
struct iovec iov[length];
|
||||
int iovidx = 0;
|
||||
int i;
|
||||
int num = offset + length;
|
||||
for (i = offset; i < num; i++) {
|
||||
jobject bufObj = (*env)->GetObjectArrayElement(env, buffers, i);
|
||||
jint pos;
|
||||
// Get the current position using the (*env)->GetIntField if possible and fallback
|
||||
// to slower (*env)->CallIntMethod(...) if needed
|
||||
if (posFieldId == NULL) {
|
||||
pos = (*env)->CallIntMethod(env, bufObj, posId, NULL);
|
||||
} else {
|
||||
pos = (*env)->GetIntField(env, bufObj, posFieldId);
|
||||
}
|
||||
jint limit;
|
||||
// Get the current limit using the (*env)->GetIntField if possible and fallback
|
||||
// to slower (*env)->CallIntMethod(...) if needed
|
||||
if (limitFieldId == NULL) {
|
||||
limit = (*env)->CallIntMethod(env, bufObj, limitId, NULL);
|
||||
} else {
|
||||
limit = (*env)->GetIntField(env, bufObj, limitFieldId);
|
||||
}
|
||||
void* buffer = (*env)->GetDirectBufferAddress(env, bufObj);
|
||||
// We check that GetDirectBufferAddress will not return NULL in OnLoad
|
||||
iov[iovidx].iov_base = buffer + pos;
|
||||
iov[iovidx].iov_len = (size_t) (limit - pos);
|
||||
iovidx++;
|
||||
|
||||
// Explicit delete local reference as otherwise the local references will only be released once the native method returns.
|
||||
// Also there may be a lot of these and JNI specification only specify that 16 must be able to be created.
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/2623
|
||||
(*env)->DeleteLocalRef(env, bufObj);
|
||||
}
|
||||
return _writev(env, clazz, fd, iov, length);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
|
||||
// We check that GetDirectBufferAddress will not return NULL in OnLoad
|
||||
return _read(env, clazz, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
|
||||
return _read(env, clazz, fd, (void*) address, pos, limit);
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz) {
|
||||
int fd[2];
|
||||
if (pipe2) {
|
||||
// we can just use pipe2 and so save extra syscalls;
|
||||
if (pipe2(fd, O_NONBLOCK) != 0) {
|
||||
return -errno;
|
||||
}
|
||||
} else {
|
||||
if (pipe(fd) == 0) {
|
||||
if (fcntl(fd[0], F_SETFD, O_NONBLOCK) < 0) {
|
||||
int err = errno;
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
return -err;
|
||||
}
|
||||
if (fcntl(fd[1], F_SETFD, O_NONBLOCK) < 0) {
|
||||
int err = errno;
|
||||
close(fd[0]);
|
||||
close(fd[1]);
|
||||
return -err;
|
||||
}
|
||||
} else {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
|
||||
// encode the fds into a 64 bit value
|
||||
return (((jlong) fd[0]) << 32) | fd[1];
|
||||
}
|
||||
|
@ -15,5 +15,16 @@
|
||||
*/
|
||||
#include <jni.h>
|
||||
|
||||
int Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd);
|
||||
int Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path);
|
||||
// Exported JNI Methods
|
||||
jint Java_io_netty_channel_unix_FileDescriptor_close(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_unix_FileDescriptor_open(JNIEnv* env, jclass clazz, jstring path);
|
||||
|
||||
jlong Java_io_netty_channel_unix_FileDescriptor_newPipe(JNIEnv* env, jclass clazz);
|
||||
|
||||
jint Java_io_netty_channel_unix_FileDescriptor_write(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
|
||||
jint Java_io_netty_channel_unix_FileDescriptor_writeAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
|
||||
jlong Java_io_netty_channel_unix_FileDescriptor_writev(JNIEnv* env, jclass clazz, jint fd, jobjectArray buffers, jint offset, jint length);
|
||||
jlong Java_io_netty_channel_unix_FileDescriptor_writevAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length);
|
||||
|
||||
jint Java_io_netty_channel_unix_FileDescriptor_read(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
|
||||
jint Java_io_netty_channel_unix_FileDescriptor_readAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
|
||||
|
715
transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.c
Normal file
715
transport-native-epoll/src/main/c/io_netty_channel_unix_Socket.c
Normal file
@ -0,0 +1,715 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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 <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include "netty_unix_errors.h"
|
||||
#include "netty_unix_socket.h"
|
||||
#include "io_netty_channel_unix_Socket.h"
|
||||
|
||||
static jclass datagramSocketAddressClass = NULL;
|
||||
static jmethodID datagramSocketAddrMethodId = NULL;
|
||||
static jmethodID inetSocketAddrMethodId = NULL;
|
||||
static jclass inetSocketAddressClass = NULL;
|
||||
static jclass netUtilClass = NULL;
|
||||
static jmethodID netUtilClassIpv4PreferredMethodId = NULL;
|
||||
static int socketType;
|
||||
static const char* ip4prefix = "::ffff:";
|
||||
|
||||
// Optional external methods
|
||||
extern int accept4(int sockFd, struct sockaddr* addr, socklen_t* addrlen, int flags) __attribute__((weak));
|
||||
|
||||
// macro to calculate the length of a sockaddr_un struct for a given path length.
|
||||
// see sys/un.h#SUN_LEN, this is modified to allow nul bytes
|
||||
#define _UNIX_ADDR_LENGTH(path_len) (uintptr_t) (((struct sockaddr_un *) 0)->sun_path) + path_len
|
||||
|
||||
static jobject createDatagramSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr, int len) {
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
jstring ipString;
|
||||
if (addr->ss_family == AF_INET) {
|
||||
struct sockaddr_in* s = (struct sockaddr_in*) addr;
|
||||
port = ntohs(s->sin_port);
|
||||
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
||||
ipString = (*env)->NewStringUTF(env, ipstr);
|
||||
} else {
|
||||
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
|
||||
port = ntohs(s->sin6_port);
|
||||
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
||||
|
||||
if (strncasecmp(ipstr, ip4prefix, 7) == 0) {
|
||||
// IPv4-mapped-on-IPv6.
|
||||
// Cut of ::ffff: prefix to workaround performance issues when parsing these
|
||||
// addresses in InetAddress.getByName(...).
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/2867
|
||||
ipString = (*env)->NewStringUTF(env, &ipstr[7]);
|
||||
} else {
|
||||
ipString = (*env)->NewStringUTF(env, ipstr);
|
||||
}
|
||||
}
|
||||
jobject socketAddr = (*env)->NewObject(env, datagramSocketAddressClass, datagramSocketAddrMethodId, ipString, port, len);
|
||||
return socketAddr;
|
||||
}
|
||||
|
||||
static int addressLength(const struct sockaddr_storage* addr) {
|
||||
if (addr->ss_family == AF_INET) {
|
||||
return 8;
|
||||
} else {
|
||||
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
|
||||
if (s->sin6_addr.s6_addr[0] == 0x00 && s->sin6_addr.s6_addr[1] == 0x00 && s->sin6_addr.s6_addr[2] == 0x00 && s->sin6_addr.s6_addr[3] == 0x00 && s->sin6_addr.s6_addr[4] == 0x00
|
||||
&& s->sin6_addr.s6_addr[5] == 0x00 && s->sin6_addr.s6_addr[6] == 0x00 && s->sin6_addr.s6_addr[7] == 0x00 && s->sin6_addr.s6_addr[8] == 0x00 && s->sin6_addr.s6_addr[9] == 0x00
|
||||
&& s->sin6_addr.s6_addr[10] == 0xff && s->sin6_addr.s6_addr[11] == 0xff) {
|
||||
// IPv4-mapped-on-IPv6
|
||||
return 8;
|
||||
} else {
|
||||
return 24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void initInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr, jbyteArray bArray, int offset, int len) {
|
||||
int port;
|
||||
if (addr->ss_family == AF_INET) {
|
||||
struct sockaddr_in* s = (struct sockaddr_in*) addr;
|
||||
port = ntohs(s->sin_port);
|
||||
|
||||
// Encode address and port into the array
|
||||
unsigned char a[4];
|
||||
a[0] = port >> 24;
|
||||
a[1] = port >> 16;
|
||||
a[2] = port >> 8;
|
||||
a[3] = port;
|
||||
(*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &s->sin_addr.s_addr);
|
||||
(*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a);
|
||||
} else {
|
||||
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
|
||||
port = ntohs(s->sin6_port);
|
||||
|
||||
if (len == 8) {
|
||||
// IPv4-mapped-on-IPv6
|
||||
// Encode port into the array and write it into the jbyteArray
|
||||
unsigned char a[4];
|
||||
a[0] = port >> 24;
|
||||
a[1] = port >> 16;
|
||||
a[2] = port >> 8;
|
||||
a[3] = port;
|
||||
|
||||
// we only need the last 4 bytes for mapped address
|
||||
(*env)->SetByteArrayRegion(env, bArray, offset, 4, (jbyte*) &(s->sin6_addr.s6_addr[12]));
|
||||
(*env)->SetByteArrayRegion(env, bArray, offset + 4, 4, (jbyte*) &a);
|
||||
} else {
|
||||
// Encode scopeid and port into the array
|
||||
unsigned char a[8];
|
||||
a[0] = s->sin6_scope_id >> 24;
|
||||
a[1] = s->sin6_scope_id >> 16;
|
||||
a[2] = s->sin6_scope_id >> 8;
|
||||
a[3] = s->sin6_scope_id;
|
||||
a[4] = port >> 24;
|
||||
a[5] = port >> 16;
|
||||
a[6] = port >> 8;
|
||||
a[7] = port;
|
||||
|
||||
(*env)->SetByteArrayRegion(env, bArray, offset, 16, (jbyte*) &(s->sin6_addr.s6_addr));
|
||||
(*env)->SetByteArrayRegion(env, bArray, offset + 16, 8, (jbyte*) &a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static jbyteArray createInetSocketAddressArray(JNIEnv* env, const struct sockaddr_storage* addr) {
|
||||
int len = addressLength(addr);
|
||||
jbyteArray bArray = (*env)->NewByteArray(env, len);
|
||||
|
||||
initInetSocketAddressArray(env, addr, bArray, 0, len);
|
||||
return bArray;
|
||||
}
|
||||
|
||||
static jobject createInetSocketAddress(JNIEnv* env, const struct sockaddr_storage* addr) {
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
int port;
|
||||
jstring ipString;
|
||||
if (addr->ss_family == AF_INET) {
|
||||
struct sockaddr_in* s = (struct sockaddr_in*) addr;
|
||||
port = ntohs(s->sin_port);
|
||||
inet_ntop(AF_INET, &s->sin_addr, ipstr, sizeof ipstr);
|
||||
ipString = (*env)->NewStringUTF(env, ipstr);
|
||||
} else {
|
||||
struct sockaddr_in6* s = (struct sockaddr_in6*) addr;
|
||||
port = ntohs(s->sin6_port);
|
||||
inet_ntop(AF_INET6, &s->sin6_addr, ipstr, sizeof ipstr);
|
||||
if (strncasecmp(ipstr, ip4prefix, 7) == 0) {
|
||||
// IPv4-mapped-on-IPv6.
|
||||
// Cut of ::ffff: prefix to workaround performance issues when parsing these
|
||||
// addresses in InetAddress.getByName(...).
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/2867
|
||||
ipString = (*env)->NewStringUTF(env, &ipstr[7]);
|
||||
} else {
|
||||
ipString = (*env)->NewStringUTF(env, ipstr);
|
||||
}
|
||||
}
|
||||
jobject socketAddr = (*env)->NewObject(env, inetSocketAddressClass, inetSocketAddrMethodId, ipString, port);
|
||||
return socketAddr;
|
||||
}
|
||||
|
||||
static int socket_type(JNIEnv* env) {
|
||||
jboolean ipv4Preferred = (*env)->CallStaticBooleanMethod(env, netUtilClass, netUtilClassIpv4PreferredMethodId);
|
||||
|
||||
if (ipv4Preferred) {
|
||||
// User asked to use ipv4 explicitly.
|
||||
return AF_INET;
|
||||
}
|
||||
int fd = socket(AF_INET6, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if (fd == -1) {
|
||||
if (errno == EAFNOSUPPORT) {
|
||||
return AF_INET;
|
||||
}
|
||||
return AF_INET6;
|
||||
} else {
|
||||
close(fd);
|
||||
return AF_INET6;
|
||||
}
|
||||
}
|
||||
|
||||
static jint _socket(JNIEnv* env, jclass clazz, int type) {
|
||||
int fd = socket(socketType, type | SOCK_NONBLOCK, 0);
|
||||
if (fd == -1) {
|
||||
return -errno;
|
||||
} else if (socketType == AF_INET6) {
|
||||
// Allow to listen /connect ipv4 and ipv6
|
||||
int optval = 0;
|
||||
if (netty_unix_socket_setOption(env, fd, IPPROTO_IPV6, IPV6_V6ONLY, &optval, sizeof(optval)) < 0) {
|
||||
// Something went wrong so close the fd and return here. setOption(...) itself throws the exception already.
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr) {
|
||||
uint16_t port = htons((uint16_t) jport);
|
||||
// Use GetPrimitiveArrayCritical and ReleasePrimitiveArrayCritical to signal the VM that we really would like
|
||||
// to not do a memory copy here. This is ok as we not do any blocking action here anyway.
|
||||
// This is important as the VM may suspend GC for the time!
|
||||
jbyte* addressBytes = (*env)->GetPrimitiveArrayCritical(env, address, 0);
|
||||
if (addressBytes == NULL) {
|
||||
// No memory left ?!?!?
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return -1;
|
||||
}
|
||||
if (socketType == AF_INET6) {
|
||||
struct sockaddr_in6* ip6addr = (struct sockaddr_in6*) addr;
|
||||
ip6addr->sin6_family = AF_INET6;
|
||||
ip6addr->sin6_port = port;
|
||||
|
||||
if (scopeId != 0) {
|
||||
ip6addr->sin6_scope_id = (uint32_t) scopeId;
|
||||
}
|
||||
memcpy(&(ip6addr->sin6_addr.s6_addr), addressBytes, 16);
|
||||
} else {
|
||||
struct sockaddr_in* ipaddr = (struct sockaddr_in*) addr;
|
||||
ipaddr->sin_family = AF_INET;
|
||||
ipaddr->sin_port = port;
|
||||
memcpy(&(ipaddr->sin_addr.s_addr), addressBytes + 12, 4);
|
||||
}
|
||||
|
||||
(*env)->ReleasePrimitiveArrayCritical(env, address, addressBytes, JNI_ABORT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static jint _sendTo(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
|
||||
struct sockaddr_storage addr;
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = sendto(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_storage));
|
||||
// keep on writing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
static jobject _recvFrom(JNIEnv* env, jint fd, void* buffer, jint pos, jint limit) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen = sizeof(addr);
|
||||
ssize_t res;
|
||||
int err;
|
||||
|
||||
do {
|
||||
res = recvfrom(fd, buffer + pos, (size_t) (limit - pos), 0, (struct sockaddr*) &addr, &addrlen);
|
||||
// Keep on reading if we was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
if (err == EAGAIN || err == EWOULDBLOCK) {
|
||||
// Nothing left to read
|
||||
return NULL;
|
||||
}
|
||||
if (err == EBADF) {
|
||||
netty_unix_errors_throwClosedChannelException(env);
|
||||
return NULL;
|
||||
}
|
||||
netty_unix_errors_throwIOExceptionErrorNo(env, "recvfrom() failed: ", err);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return createDatagramSocketAddress(env, &addr, res);
|
||||
}
|
||||
|
||||
int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen) {
|
||||
int rc = getsockopt(fd, level, optname, optval, &optlen);
|
||||
if (rc < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "getsockopt() failed: ", errno);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len) {
|
||||
int rc = setsockopt(fd, level, optname, optval, len);
|
||||
if (rc < 0) {
|
||||
netty_unix_errors_throwChannelExceptionErrorNo(env, "setsockopt() failed: ", errno);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env) {
|
||||
jclass localDatagramSocketAddressClass = (*env)->FindClass(env, "io/netty/channel/unix/DatagramSocketAddress");
|
||||
if (localDatagramSocketAddressClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
datagramSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localDatagramSocketAddressClass);
|
||||
if (datagramSocketAddressClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
datagramSocketAddrMethodId = (*env)->GetMethodID(env, datagramSocketAddressClass, "<init>", "(Ljava/lang/String;II)V");
|
||||
if (datagramSocketAddrMethodId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: DatagramSocketAddress.<init>(String, int, int)");
|
||||
return JNI_ERR;
|
||||
}
|
||||
jclass localInetSocketAddressClass = (*env)->FindClass(env, "java/net/InetSocketAddress");
|
||||
if (localInetSocketAddressClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
inetSocketAddressClass = (jclass) (*env)->NewGlobalRef(env, localInetSocketAddressClass);
|
||||
if (inetSocketAddressClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
inetSocketAddrMethodId = (*env)->GetMethodID(env, inetSocketAddressClass, "<init>", "(Ljava/lang/String;I)V");
|
||||
if (inetSocketAddrMethodId == NULL) {
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: InetSocketAddress.<init>(String, int)");
|
||||
return JNI_ERR;
|
||||
}
|
||||
jclass localNetUtilClass = (*env)->FindClass(env, "io/netty/util/NetUtil" );
|
||||
if (localNetUtilClass == NULL) {
|
||||
// pending exception...
|
||||
return JNI_ERR;
|
||||
}
|
||||
netUtilClass = (jclass) (*env)->NewGlobalRef(env, localNetUtilClass);
|
||||
if (netUtilClass == NULL) {
|
||||
// out-of-memory!
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
netUtilClassIpv4PreferredMethodId = (*env)->GetStaticMethodID(env, netUtilClass, "isIpV4StackPreferred", "()Z" );
|
||||
if (netUtilClassIpv4PreferredMethodId == NULL) {
|
||||
// position method was not found.. something is wrong so bail out
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get method ID: NetUild.isIpV4StackPreferred()");
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
void* mem = malloc(1);
|
||||
if (mem == NULL) {
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
jobject directBuffer = (*env)->NewDirectByteBuffer(env, mem, 1);
|
||||
if (directBuffer == NULL) {
|
||||
free(mem);
|
||||
|
||||
netty_unix_errors_throwOutOfMemoryError(env);
|
||||
return JNI_ERR;
|
||||
}
|
||||
if ((*env)->GetDirectBufferAddress(env, directBuffer) == NULL) {
|
||||
free(mem);
|
||||
|
||||
netty_unix_errors_throwRuntimeException(env, "failed to get direct buffer address");
|
||||
return JNI_ERR;
|
||||
}
|
||||
free(mem);
|
||||
|
||||
socketType = socket_type(env);
|
||||
}
|
||||
|
||||
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env) {
|
||||
if (datagramSocketAddressClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, datagramSocketAddressClass);
|
||||
datagramSocketAddressClass = NULL;
|
||||
}
|
||||
if (inetSocketAddressClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, inetSocketAddressClass);
|
||||
inetSocketAddressClass = NULL;
|
||||
}
|
||||
if (netUtilClass != NULL) {
|
||||
(*env)->DeleteGlobalRef(env, netUtilClass);
|
||||
netUtilClass = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write) {
|
||||
int mode;
|
||||
if (read && write) {
|
||||
mode = SHUT_RDWR;
|
||||
} else if (read) {
|
||||
mode = SHUT_RD;
|
||||
} else if (write) {
|
||||
mode = SHUT_WR;
|
||||
} else {
|
||||
return -EINVAL;
|
||||
}
|
||||
if (shutdown(fd, mode) < 0) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
|
||||
struct sockaddr_storage addr;
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr*) &addr, sizeof(addr)) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog) {
|
||||
if (listen(fd, backlog) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port) {
|
||||
struct sockaddr_storage addr;
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
|
||||
// A runtime exception was thrown
|
||||
return -1;
|
||||
}
|
||||
|
||||
int res;
|
||||
int err;
|
||||
do {
|
||||
res = connect(fd, (struct sockaddr*) &addr, sizeof(addr));
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd) {
|
||||
// connect may be done
|
||||
// return true if connection finished successfully
|
||||
// return false if connection is still in progress
|
||||
// throw exception if connection failed
|
||||
int optval;
|
||||
int res = netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval));
|
||||
if (res != 0) {
|
||||
// getOption failed
|
||||
return -1;
|
||||
}
|
||||
if (optval == 0) {
|
||||
// connect succeeded
|
||||
return 0;
|
||||
}
|
||||
return -optval;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress) {
|
||||
jint socketFd;
|
||||
int err;
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t address_len = sizeof(addr);
|
||||
|
||||
do {
|
||||
if (accept4) {
|
||||
socketFd = accept4(fd, (struct sockaddr*) &addr, &address_len, SOCK_NONBLOCK | SOCK_CLOEXEC);
|
||||
} else {
|
||||
socketFd = accept(fd, (struct sockaddr*) &addr, &address_len);
|
||||
}
|
||||
} while (socketFd == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (socketFd == -1) {
|
||||
return -err;
|
||||
}
|
||||
|
||||
int len = addressLength(&addr);
|
||||
|
||||
// Fill in remote address details
|
||||
(*env)->SetByteArrayRegion(env, acceptedAddress, 0, 4, (jbyte*) &len);
|
||||
initInetSocketAddressArray(env, &addr, acceptedAddress, 1, len);
|
||||
|
||||
if (accept4) {
|
||||
return socketFd;
|
||||
} else {
|
||||
// accept4 was not present so need two more sys-calls ...
|
||||
if (fcntl(socketFd, F_SETFD, FD_CLOEXEC) == -1 || fcntl(socketFd, F_SETFL, O_NONBLOCK) == -1) {
|
||||
return -errno;
|
||||
}
|
||||
}
|
||||
return socketFd;
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
if (getpeername(fd, (struct sockaddr*) &addr, &len) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return createInetSocketAddressArray(env, &addr);
|
||||
}
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd) {
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
if (getsockname(fd, (struct sockaddr*) &addr, &len) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
return createInetSocketAddressArray(env, &addr);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz) {
|
||||
return _socket(env, clazz, SOCK_DGRAM);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz) {
|
||||
return _socket(env, clazz, SOCK_STREAM);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz) {
|
||||
int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
||||
if (fd == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port) {
|
||||
// We check that GetDirectBufferAddress will not return NULL in OnLoad
|
||||
return _sendTo(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit, address, scopeId, port);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit ,jbyteArray address, jint scopeId, jint port) {
|
||||
return _sendTo(env, fd, (void*) memoryAddress, pos, limit, address, scopeId, port);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port) {
|
||||
struct sockaddr_storage addr;
|
||||
|
||||
if (netty_unix_socket_initSockaddr(env, address, scopeId, port, &addr) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct msghdr m;
|
||||
m.msg_name = (void*) &addr;
|
||||
m.msg_namelen = (socklen_t) sizeof(struct sockaddr_storage);
|
||||
m.msg_iov = (struct iovec*) memoryAddress;
|
||||
m.msg_iovlen = length;
|
||||
|
||||
ssize_t res;
|
||||
int err;
|
||||
do {
|
||||
res = sendmsg(fd, &m, 0);
|
||||
// keep on writing if it was interrupted
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return (jint) res;
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit) {
|
||||
// We check that GetDirectBufferAddress will not return NULL in OnLoad
|
||||
return _recvFrom(env, fd, (*env)->GetDirectBufferAddress(env, jbuffer), pos, limit);
|
||||
}
|
||||
|
||||
JNIEXPORT jobject JNICALL Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit) {
|
||||
return _recvFrom(env, fd, (void*) address, pos, limit);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
|
||||
struct sockaddr_un addr;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
|
||||
jint socket_path_len = (*env)->GetArrayLength(env, socketPath);
|
||||
if (socket_path_len > sizeof(addr.sun_path)) {
|
||||
socket_path_len = sizeof(addr.sun_path);
|
||||
}
|
||||
memcpy(addr.sun_path, socket_path, socket_path_len);
|
||||
|
||||
if (unlink(socket_path) == -1 && errno != ENOENT) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
int res = bind(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
|
||||
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
|
||||
|
||||
if (res == -1) {
|
||||
return -errno;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jbyteArray socketPath) {
|
||||
struct sockaddr_un addr;
|
||||
jint socket_path_len;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
|
||||
jbyte* socket_path = (*env)->GetByteArrayElements(env, socketPath, 0);
|
||||
socket_path_len = (*env)->GetArrayLength(env, socketPath);
|
||||
if (socket_path_len > sizeof(addr.sun_path)) {
|
||||
socket_path_len = sizeof(addr.sun_path);
|
||||
}
|
||||
memcpy(addr.sun_path, socket_path, socket_path_len);
|
||||
|
||||
int res;
|
||||
int err;
|
||||
do {
|
||||
res = connect(fd, (struct sockaddr*) &addr, _UNIX_ADDR_LENGTH(socket_path_len));
|
||||
} while (res == -1 && ((err = errno) == EINTR));
|
||||
|
||||
(*env)->ReleaseByteArrayElements(env, socketPath, socket_path, 0);
|
||||
|
||||
if (res < 0) {
|
||||
return -err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval) {
|
||||
struct linger solinger;
|
||||
if (optval < 0) {
|
||||
solinger.l_onoff = 0;
|
||||
solinger.l_linger = 0;
|
||||
} else {
|
||||
solinger.l_onoff = 1;
|
||||
solinger.l_linger = optval;
|
||||
}
|
||||
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_RCVBUF, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_SNDBUF, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_CORK, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd) {
|
||||
struct linger optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_LINGER, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if (optval.l_onoff == 0) {
|
||||
return -1;
|
||||
} else {
|
||||
return optval.l_linger;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd) {
|
||||
int optval;
|
||||
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_ERROR, &optval, sizeof(optval)) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return optval;
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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 <jni.h>
|
||||
|
||||
// Exported JNI Methods
|
||||
jint Java_io_netty_channel_unix_Socket_shutdown(JNIEnv* env, jclass clazz, jint fd, jboolean read, jboolean write);
|
||||
|
||||
jint Java_io_netty_channel_unix_Socket_newSocketStreamFd(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Socket_newSocketDgramFd(JNIEnv* env, jclass clazz);
|
||||
jint Java_io_netty_channel_unix_Socket_newSocketDomainFd(JNIEnv* env, jclass clazz);
|
||||
|
||||
jint Java_io_netty_channel_unix_Socket_sendTo(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_unix_Socket_sendToAddress(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint pos, jint limit, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_unix_Socket_sendToAddresses(JNIEnv* env, jclass clazz, jint fd, jlong memoryAddress, jint length, jbyteArray address, jint scopeId, jint port);
|
||||
|
||||
jobject Java_io_netty_channel_unix_Socket_recvFrom(JNIEnv* env, jclass clazz, jint fd, jobject jbuffer, jint pos, jint limit);
|
||||
jobject Java_io_netty_channel_unix_Socket_recvFromAddress(JNIEnv* env, jclass clazz, jint fd, jlong address, jint pos, jint limit);
|
||||
|
||||
jint Java_io_netty_channel_unix_Socket_bind(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_unix_Socket_bindDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
|
||||
jint Java_io_netty_channel_unix_Socket_listen(JNIEnv* env, jclass clazz, jint fd, jint backlog);
|
||||
jint Java_io_netty_channel_unix_Socket_accept(JNIEnv* env, jclass clazz, jint fd, jbyteArray acceptedAddress);
|
||||
|
||||
jint Java_io_netty_channel_unix_Socket_connect(JNIEnv* env, jclass clazz, jint fd, jbyteArray address, jint scopeId, jint port);
|
||||
jint Java_io_netty_channel_unix_Socket_connectDomainSocket(JNIEnv* env, jclass clazz, jint fd, jstring address);
|
||||
jint Java_io_netty_channel_unix_Socket_finishConnect(JNIEnv* env, jclass clazz, jint fd);
|
||||
|
||||
jbyteArray Java_io_netty_channel_unix_Socket_remoteAddress(JNIEnv* env, jclass clazz, jint fd);
|
||||
jbyteArray Java_io_netty_channel_unix_Socket_localAddress(JNIEnv* env, jclass clazz, jint fd);
|
||||
|
||||
void Java_io_netty_channel_unix_Socket_setTcpNoDelay(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_unix_Socket_setReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_unix_Socket_setSendBufferSize(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_unix_Socket_setKeepAlive(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_unix_Socket_setTcpCork(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
void Java_io_netty_channel_unix_Socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, jint optval);
|
||||
|
||||
jint Java_io_netty_channel_unix_Socket_isTcpNoDelay(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_unix_Socket_getReceiveBufferSize(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_unix_Socket_getSendBufferSize(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_unix_Socket_isTcpCork(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_unix_Socket_getSoLinger(JNIEnv* env, jclass clazz, jint fd);
|
||||
jint Java_io_netty_channel_unix_Socket_getSoError(JNIEnv* env, jclass clazz, jint fd);
|
33
transport-native-epoll/src/main/c/netty_unix_errors.h
Normal file
33
transport-native-epoll/src/main/c/netty_unix_errors.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
#ifndef NETTY_UNIX_ERRORS_H_
|
||||
#define NETTY_UNIX_ERRORS_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
void netty_unix_errors_throwRuntimeException(JNIEnv* env, char* message);
|
||||
void netty_unix_errors_throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
|
||||
void netty_unix_errors_throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
|
||||
void netty_unix_errors_throwIOException(JNIEnv* env, char* message);
|
||||
void netty_unix_errors_throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
|
||||
void netty_unix_errors_throwClosedChannelException(JNIEnv* env);
|
||||
void netty_unix_errors_throwOutOfMemoryError(JNIEnv* env);
|
||||
|
||||
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
||||
jint netty_unix_errors_JNI_OnLoad(JNIEnv* env);
|
||||
void netty_unix_errors_JNI_OnUnLoad(JNIEnv* env);
|
||||
|
||||
#endif /* NETTY_UNIX_ERRORS_H_ */
|
@ -13,12 +13,13 @@
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
#ifndef NETTY_UNIX_FILEDESCRIPTOR_H_
|
||||
#define NETTY_UNIX_FILEDESCRIPTOR_H_
|
||||
|
||||
void throwRuntimeException(JNIEnv* env, char* message);
|
||||
void throwRuntimeExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
|
||||
void throwChannelExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
|
||||
void throwIOException(JNIEnv* env, char* message);
|
||||
void throwIOExceptionErrorNo(JNIEnv* env, char* message, int errorNumber);
|
||||
void throwClosedChannelException(JNIEnv* env);
|
||||
void throwOutOfMemoryError(JNIEnv* env);
|
||||
char* exceptionMessage(char* msg, int error);
|
||||
#include <jni.h>
|
||||
|
||||
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
||||
jint netty_unix_filedescriptor_JNI_OnLoad(JNIEnv* env);
|
||||
void netty_unix_filedescriptor_JNI_OnUnLoad(JNIEnv* env);
|
||||
|
||||
#endif /* NETTY_UNIX_FILEDESCRIPTOR_H_ */
|
31
transport-native-epoll/src/main/c/netty_unix_socket.h
Normal file
31
transport-native-epoll/src/main/c/netty_unix_socket.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
#ifndef NETTY_UNIX_SOCKET_H_
|
||||
#define NETTY_UNIX_SOCKET_H_
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <jni.h>
|
||||
|
||||
// External C methods
|
||||
int netty_unix_socket_initSockaddr(JNIEnv* env, jbyteArray address, jint scopeId, jint jport, const struct sockaddr_storage* addr);
|
||||
int netty_unix_socket_getOption(JNIEnv* env, jint fd, int level, int optname, void* optval, socklen_t optlen);
|
||||
int netty_unix_socket_setOption(JNIEnv* env, jint fd, int level, int optname, const void* optval, socklen_t len);
|
||||
|
||||
// JNI initialization hooks. Users of this file are responsible for calling these in the JNI_OnLoad and JNI_OnUnload methods.
|
||||
jint netty_unix_socket_JNI_OnLoad(JNIEnv* env);
|
||||
void netty_unix_socket_JNI_OnUnLoad(JNIEnv* env);
|
||||
|
||||
#endif /* NETTY_UNIX_SOCKET_H_ */
|
@ -27,6 +27,7 @@ import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.socket.ChannelInputShutdownEvent;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.channel.unix.UnixChannel;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import io.netty.util.internal.OneTimeTask;
|
||||
@ -36,32 +37,27 @@ import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.UnresolvedAddressException;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
abstract class AbstractEpollChannel extends AbstractChannel implements UnixChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false);
|
||||
private final int readFlag;
|
||||
private final FileDescriptor fileDescriptor;
|
||||
private final Socket fileDescriptor;
|
||||
protected int flags = Native.EPOLLET;
|
||||
|
||||
protected volatile boolean active;
|
||||
private volatile boolean inputShutdown;
|
||||
|
||||
AbstractEpollChannel(int fd, int flag) {
|
||||
AbstractEpollChannel(Socket fd, int flag) {
|
||||
this(null, fd, flag, false);
|
||||
}
|
||||
|
||||
AbstractEpollChannel(Channel parent, int fd, int flag, boolean active) {
|
||||
this(parent, new FileDescriptor(fd), flag, active);
|
||||
}
|
||||
|
||||
AbstractEpollChannel(Channel parent, FileDescriptor fd, int flag, boolean active) {
|
||||
AbstractEpollChannel(Channel parent, Socket fd, int flag, boolean active) {
|
||||
super(parent);
|
||||
if (fd == null) {
|
||||
throw new NullPointerException("fd");
|
||||
}
|
||||
fileDescriptor = checkNotNull(fd, "fd");
|
||||
readFlag = flag;
|
||||
flags |= flag;
|
||||
this.active = active;
|
||||
fileDescriptor = fd;
|
||||
}
|
||||
|
||||
void setFlag(int flag) throws IOException {
|
||||
@ -83,7 +79,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileDescriptor fd() {
|
||||
public final Socket fd() {
|
||||
return fileDescriptor;
|
||||
}
|
||||
|
||||
@ -109,7 +105,11 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
// deregister from epoll now and shutdown the socket.
|
||||
doDeregister();
|
||||
if (active) {
|
||||
shutdown(fd.intValue());
|
||||
try {
|
||||
fd().shutdown(true, true);
|
||||
} catch (IOException ignored) {
|
||||
// The FD will be closed, so if shutdown fails there is nothing we can do.
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
// Ensure the file descriptor is closed in all cases.
|
||||
@ -117,14 +117,6 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on {@link #doClose()} before the actual {@link FileDescriptor} is closed.
|
||||
* This implementation does nothing.
|
||||
*/
|
||||
protected void shutdown(int fd) throws IOException {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDisconnect() throws Exception {
|
||||
doClose();
|
||||
@ -253,11 +245,10 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
int localReadAmount;
|
||||
unsafe().recvBufAllocHandle().attemptedBytesRead(byteBuf.writableBytes());
|
||||
if (byteBuf.hasMemoryAddress()) {
|
||||
localReadAmount = Native.readAddress(
|
||||
fileDescriptor.intValue(), byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
|
||||
localReadAmount = fileDescriptor.readAddress(byteBuf.memoryAddress(), writerIndex, byteBuf.capacity());
|
||||
} else {
|
||||
ByteBuffer buf = byteBuf.internalNioBuffer(writerIndex, byteBuf.writableBytes());
|
||||
localReadAmount = Native.read(fileDescriptor.intValue(), buf, buf.position(), buf.limit());
|
||||
localReadAmount = fileDescriptor.read(buf, buf.position(), buf.limit());
|
||||
}
|
||||
if (localReadAmount > 0) {
|
||||
byteBuf.writerIndex(writerIndex + localReadAmount);
|
||||
@ -273,8 +264,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
int readerIndex = buf.readerIndex();
|
||||
int writerIndex = buf.writerIndex();
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
int localFlushedAmount = Native.writeAddress(
|
||||
fileDescriptor.intValue(), memoryAddress, readerIndex, writerIndex);
|
||||
int localFlushedAmount = fileDescriptor.writeAddress(memoryAddress, readerIndex, writerIndex);
|
||||
if (localFlushedAmount > 0) {
|
||||
writtenBytes += localFlushedAmount;
|
||||
if (writtenBytes == readableBytes) {
|
||||
@ -295,7 +285,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
int pos = nioBuf.position();
|
||||
int limit = nioBuf.limit();
|
||||
int localFlushedAmount = Native.write(fileDescriptor.intValue(), nioBuf, pos, limit);
|
||||
int localFlushedAmount = fileDescriptor.write(nioBuf, pos, limit);
|
||||
if (localFlushedAmount > 0) {
|
||||
nioBuf.position(pos + localFlushedAmount);
|
||||
writtenBytes += localFlushedAmount;
|
||||
@ -382,7 +372,7 @@ abstract class AbstractEpollChannel extends AbstractChannel implements UnixChann
|
||||
if (isOpen()) {
|
||||
if (Boolean.TRUE.equals(config().getOption(ChannelOption.ALLOW_HALF_CLOSURE))) {
|
||||
try {
|
||||
Native.shutdown(fd().intValue(), true, false);
|
||||
fd().shutdown(true, false);
|
||||
clearEpollIn0();
|
||||
pipeline().fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE);
|
||||
} catch (IOException e) {
|
||||
|
@ -25,6 +25,7 @@ import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.ServerChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
@ -32,12 +33,31 @@ import java.net.SocketAddress;
|
||||
public abstract class AbstractEpollServerChannel extends AbstractEpollChannel implements ServerChannel {
|
||||
private static final ChannelMetadata METADATA = new ChannelMetadata(false, 16);
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
|
||||
*/
|
||||
protected AbstractEpollServerChannel(int fd) {
|
||||
super(fd, Native.EPOLLIN);
|
||||
this(new Socket(fd), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollServerChannel(FileDescriptor fd) {
|
||||
super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0);
|
||||
this(new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollServerChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollServerChannel(Socket fd) {
|
||||
this(fd, fd.getSoError() == 0);
|
||||
}
|
||||
|
||||
protected AbstractEpollServerChannel(Socket fd, boolean active) {
|
||||
super(null, fd, Native.EPOLLIN, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -109,7 +129,7 @@ public abstract class AbstractEpollServerChannel extends AbstractEpollChannel im
|
||||
try {
|
||||
try {
|
||||
do {
|
||||
int socketFd = Native.accept(fd().intValue(), acceptedAddress);
|
||||
int socketFd = fd().accept(acceptedAddress);
|
||||
if (socketFd == -1) {
|
||||
// this means everything was handled for now
|
||||
break;
|
||||
|
@ -15,7 +15,6 @@
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.buffer.CompositeByteBuf;
|
||||
@ -31,6 +30,7 @@ import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
import io.netty.util.internal.MpscLinkedQueueNode;
|
||||
import io.netty.util.internal.OneTimeTask;
|
||||
@ -47,6 +47,9 @@ import java.util.Queue;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static io.netty.channel.unix.FileDescriptor.pipe;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
|
||||
private static final String EXPECTED_TYPES =
|
||||
@ -71,33 +74,53 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
private volatile boolean outputShutdown;
|
||||
|
||||
// Lazy init these if we need to splice(...)
|
||||
private int pipeIn = -1;
|
||||
private int pipeOut = -1;
|
||||
private FileDescriptor pipeIn;
|
||||
private FileDescriptor pipeOut;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Channel, Socket)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(Channel parent, int fd) {
|
||||
this(parent, new Socket(fd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(int fd) {
|
||||
this(new Socket(fd));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(FileDescriptor fd) {
|
||||
this(new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractEpollStreamChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected AbstractEpollStreamChannel(Socket fd) {
|
||||
this(fd, fd.getSoError() == 0);
|
||||
}
|
||||
|
||||
protected AbstractEpollStreamChannel(Channel parent, Socket fd) {
|
||||
super(parent, fd, Native.EPOLLIN, true);
|
||||
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
|
||||
flags |= Native.EPOLLRDHUP;
|
||||
}
|
||||
|
||||
protected AbstractEpollStreamChannel(int fd) {
|
||||
super(fd, Native.EPOLLIN);
|
||||
protected AbstractEpollStreamChannel(Socket fd, boolean active) {
|
||||
super(null, fd, Native.EPOLLIN, active);
|
||||
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
|
||||
flags |= Native.EPOLLRDHUP;
|
||||
}
|
||||
|
||||
protected AbstractEpollStreamChannel(FileDescriptor fd) {
|
||||
super(null, fd, Native.EPOLLIN, Native.getSoError(fd.intValue()) == 0);
|
||||
|
||||
// Add EPOLLRDHUP so we are notified once the remote peer close the connection.
|
||||
flags |= Native.EPOLLRDHUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdown(int fd) throws IOException {
|
||||
Native.shutdown(fd, true, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractEpollUnsafe newUnsafe() {
|
||||
return new EpollStreamUnsafe();
|
||||
@ -262,7 +285,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
int offset = 0;
|
||||
int end = offset + cnt;
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
long localWrittenBytes = Native.writevAddresses(fd().intValue(), array.memoryAddress(offset), cnt);
|
||||
long localWrittenBytes = fd().writevAddresses(array.memoryAddress(offset), cnt);
|
||||
if (localWrittenBytes == 0) {
|
||||
break;
|
||||
}
|
||||
@ -301,7 +324,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
int offset = 0;
|
||||
int end = offset + nioBufferCnt;
|
||||
for (int i = writeSpinCount - 1; i >= 0; i--) {
|
||||
long localWrittenBytes = Native.writev(fd().intValue(), nioBuffers, offset, nioBufferCnt);
|
||||
long localWrittenBytes = fd().writev(nioBuffers, offset, nioBufferCnt);
|
||||
if (localWrittenBytes == 0) {
|
||||
break;
|
||||
}
|
||||
@ -511,7 +534,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
|
||||
protected void shutdownOutput0(final ChannelPromise promise) {
|
||||
try {
|
||||
Native.shutdown(fd().intValue(), false, true);
|
||||
fd().shutdown(false, true);
|
||||
outputShutdown = true;
|
||||
promise.setSuccess();
|
||||
} catch (Throwable cause) {
|
||||
@ -558,12 +581,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
*/
|
||||
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
|
||||
if (localAddress != null) {
|
||||
Native.bind(fd().intValue(), localAddress);
|
||||
fd().bind(localAddress);
|
||||
}
|
||||
|
||||
boolean success = false;
|
||||
try {
|
||||
boolean connected = Native.connect(fd().intValue(), remoteAddress);
|
||||
boolean connected = fd().connect(remoteAddress);
|
||||
if (!connected) {
|
||||
setFlag(Native.EPOLLOUT);
|
||||
}
|
||||
@ -576,10 +599,10 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
}
|
||||
}
|
||||
|
||||
private void safeClosePipe(int pipe) {
|
||||
if (pipe != -1) {
|
||||
private void safeClosePipe(FileDescriptor fd) {
|
||||
if (fd != null) {
|
||||
try {
|
||||
Native.close(pipe);
|
||||
fd.close();
|
||||
} catch (IOException e) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Error while closing a pipe", e);
|
||||
@ -735,7 +758,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
* Finish the connect
|
||||
*/
|
||||
private boolean doFinishConnect() throws Exception {
|
||||
if (Native.finishConnect(fd().intValue())) {
|
||||
if (fd().finishConnect()) {
|
||||
clearFlag(Native.EPOLLOUT);
|
||||
return true;
|
||||
} else {
|
||||
@ -840,13 +863,13 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
|
||||
abstract boolean spliceIn(RecvByteBufAllocator.Handle handle) throws IOException;
|
||||
|
||||
protected final int spliceIn(int pipeOut, RecvByteBufAllocator.Handle handle) throws IOException {
|
||||
protected final int spliceIn(FileDescriptor pipeOut, RecvByteBufAllocator.Handle handle) throws IOException {
|
||||
// calculate the maximum amount of data we are allowed to splice
|
||||
int length = Math.min(handle.guess(), len);
|
||||
int splicedIn = 0;
|
||||
for (;;) {
|
||||
// Splicing until there is nothing left to splice.
|
||||
int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut, -1, length);
|
||||
int localSplicedIn = Native.splice(fd().intValue(), -1, pipeOut.intValue(), -1, length);
|
||||
if (localSplicedIn == 0) {
|
||||
break;
|
||||
}
|
||||
@ -885,12 +908,12 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
// We create the pipe on the target channel as this will allow us to just handle pending writes
|
||||
// later in a correct fashion without get into any ordering issues when spliceTo(...) is called
|
||||
// on multiple Channels pointing to one target Channel.
|
||||
int pipeOut = ch.pipeOut;
|
||||
if (pipeOut == -1) {
|
||||
FileDescriptor pipeOut = ch.pipeOut;
|
||||
if (pipeOut == null) {
|
||||
// Create a new pipe as non was created before.
|
||||
long fds = Native.pipe();
|
||||
ch.pipeIn = (int) (fds >> 32);
|
||||
pipeOut = ch.pipeOut = (int) fds;
|
||||
FileDescriptor[] pipe = pipe();
|
||||
ch.pipeIn = pipe[0];
|
||||
pipeOut = ch.pipeOut = pipe[1];
|
||||
}
|
||||
|
||||
int splicedIn = spliceIn(pipeOut, handle);
|
||||
@ -946,7 +969,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
public boolean spliceOut() throws Exception {
|
||||
assert ch.eventLoop().inEventLoop();
|
||||
try {
|
||||
int splicedOut = Native.splice(ch.pipeIn, -1, ch.fd().intValue(), -1, len);
|
||||
int splicedOut = Native.splice(ch.pipeIn.intValue(), -1, ch.fd().intValue(), -1, len);
|
||||
len -= splicedOut;
|
||||
if (len == 0) {
|
||||
if (autoRead) {
|
||||
@ -991,13 +1014,11 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
return true;
|
||||
}
|
||||
|
||||
int pipeIn = -1;
|
||||
int pipeOut = -1;
|
||||
try {
|
||||
long fds = Native.pipe();
|
||||
pipeIn = (int) (fds >> 32);
|
||||
pipeOut = (int) fds;
|
||||
|
||||
FileDescriptor[] pipe = pipe();
|
||||
FileDescriptor pipeIn = pipe[0];
|
||||
FileDescriptor pipeOut = pipe[1];
|
||||
try {
|
||||
int splicedIn = spliceIn(pipeOut, handle);
|
||||
if (splicedIn > 0) {
|
||||
// Integer.MAX_VALUE is a special value which will result in splice forever.
|
||||
@ -1005,7 +1026,7 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
len -= splicedIn;
|
||||
}
|
||||
do {
|
||||
int splicedOut = Native.splice(pipeIn, -1, fd.intValue(), offset, splicedIn);
|
||||
int splicedOut = Native.splice(pipeIn.intValue(), -1, fd.intValue(), offset, splicedIn);
|
||||
splicedIn -= splicedOut;
|
||||
} while (splicedIn > 0);
|
||||
if (len == 0) {
|
||||
@ -1014,13 +1035,14 @@ public abstract class AbstractEpollStreamChannel extends AbstractEpollChannel {
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
return true;
|
||||
} finally {
|
||||
safeClosePipe(pipeIn);
|
||||
safeClosePipe(pipeOut);
|
||||
}
|
||||
} catch (Throwable cause) {
|
||||
promise.setFailure(cause);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
|
||||
/**
|
||||
* Tells if <a href="http://netty.io/wiki/native-transports.html">{@code netty-transport-native-epoll}</a> is supported.
|
||||
*/
|
||||
@ -24,24 +26,24 @@ public final class Epoll {
|
||||
|
||||
static {
|
||||
Throwable cause = null;
|
||||
int epollFd = -1;
|
||||
int eventFd = -1;
|
||||
FileDescriptor epollFd = null;
|
||||
FileDescriptor eventFd = null;
|
||||
try {
|
||||
epollFd = Native.epollCreate();
|
||||
eventFd = Native.eventFd();
|
||||
epollFd = Native.newEpollCreate();
|
||||
eventFd = Native.newEventFd();
|
||||
} catch (Throwable t) {
|
||||
cause = t;
|
||||
} finally {
|
||||
if (epollFd != -1) {
|
||||
if (epollFd != null) {
|
||||
try {
|
||||
Native.close(epollFd);
|
||||
epollFd.close();
|
||||
} catch (Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (eventFd != -1) {
|
||||
if (eventFd != null) {
|
||||
try {
|
||||
Native.close(eventFd);
|
||||
eventFd.close();
|
||||
} catch (Exception ignore) {
|
||||
// ignore
|
||||
}
|
||||
|
@ -30,7 +30,9 @@ import io.netty.channel.RecvByteBufAllocator;
|
||||
import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.DatagramChannelConfig;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.channel.unix.DatagramSocketAddress;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
||||
@ -45,6 +47,8 @@ import java.nio.channels.NotYetConnectedException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDgram;
|
||||
|
||||
/**
|
||||
* {@link DatagramChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
|
||||
* maximal performance.
|
||||
@ -64,20 +68,24 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
private final EpollDatagramChannelConfig config;
|
||||
|
||||
public EpollDatagramChannel() {
|
||||
super(Native.socketDgramFd(), Native.EPOLLIN);
|
||||
super(newSocketDgram(), Native.EPOLLIN);
|
||||
config = new EpollDatagramChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link EpollDatagramChannel} from the given {@link FileDescriptor}.
|
||||
* @deprecated Use {@link #EpollDatagramChannel(Socket)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollDatagramChannel(FileDescriptor fd) {
|
||||
super(null, fd, Native.EPOLLIN, true);
|
||||
config = new EpollDatagramChannelConfig(this);
|
||||
this(new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
public EpollDatagramChannel(Socket fd) {
|
||||
super(null, fd, Native.EPOLLIN, true);
|
||||
// As we create an EpollDatagramChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
local = Native.localAddress(fd.intValue());
|
||||
local = fd.localAddress();
|
||||
config = new EpollDatagramChannelConfig(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -276,9 +284,8 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
InetSocketAddress addr = (InetSocketAddress) localAddress;
|
||||
checkResolvable(addr);
|
||||
int fd = fd().intValue();
|
||||
Native.bind(fd, addr);
|
||||
local = Native.localAddress(fd);
|
||||
fd().bind(addr);
|
||||
local = fd().localAddress();
|
||||
active = true;
|
||||
}
|
||||
|
||||
@ -372,18 +379,18 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
final int writtenBytes;
|
||||
if (data.hasMemoryAddress()) {
|
||||
long memoryAddress = data.memoryAddress();
|
||||
writtenBytes = Native.sendToAddress(fd().intValue(), memoryAddress, data.readerIndex(), data.writerIndex(),
|
||||
writtenBytes = fd().sendToAddress(memoryAddress, data.readerIndex(), data.writerIndex(),
|
||||
remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
} else if (data instanceof CompositeByteBuf) {
|
||||
IovArray array = IovArrayThreadLocal.get((CompositeByteBuf) data);
|
||||
int cnt = array.count();
|
||||
assert cnt != 0;
|
||||
|
||||
writtenBytes = Native.sendToAddresses(fd().intValue(), array.memoryAddress(0),
|
||||
writtenBytes = fd().sendToAddresses(array.memoryAddress(0),
|
||||
cnt, remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
} else {
|
||||
ByteBuffer nioData = data.internalNioBuffer(data.readerIndex(), data.readableBytes());
|
||||
writtenBytes = Native.sendTo(fd().intValue(), nioData, nioData.position(), nioData.limit(),
|
||||
writtenBytes = fd().sendTo(nioData, nioData.position(), nioData.limit(),
|
||||
remoteAddress.getAddress(), remoteAddress.getPort());
|
||||
}
|
||||
|
||||
@ -490,7 +497,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
|
||||
checkResolvable(remoteAddress);
|
||||
EpollDatagramChannel.this.remote = remoteAddress;
|
||||
EpollDatagramChannel.this.local = Native.localAddress(fd().intValue());
|
||||
EpollDatagramChannel.this.local = fd().localAddress();
|
||||
success = true;
|
||||
|
||||
// Regardless if the connection attempt was cancelled, channelActive() event should be triggered,
|
||||
@ -543,12 +550,11 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
final DatagramSocketAddress remoteAddress;
|
||||
if (data.hasMemoryAddress()) {
|
||||
// has a memory address so use optimized call
|
||||
remoteAddress = Native.recvFromAddress(
|
||||
fd().intValue(), data.memoryAddress(), data.writerIndex(), data.capacity());
|
||||
remoteAddress = fd().recvFromAddress(data.memoryAddress(), data.writerIndex(),
|
||||
data.capacity());
|
||||
} else {
|
||||
ByteBuffer nioData = data.internalNioBuffer(data.writerIndex(), data.writableBytes());
|
||||
remoteAddress = Native.recvFrom(
|
||||
fd().intValue(), nioData, nioData.position(), nioData.limit());
|
||||
remoteAddress = fd().recvFrom(nioData, nioData.position(), nioData.limit());
|
||||
}
|
||||
|
||||
if (remoteAddress == null) {
|
||||
@ -558,7 +564,7 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
}
|
||||
|
||||
allocHandle.incMessagesRead(1);
|
||||
allocHandle.lastBytesRead(remoteAddress.receivedAmount);
|
||||
allocHandle.lastBytesRead(remoteAddress.receivedAmount());
|
||||
data.writerIndex(data.writerIndex() + allocHandle.lastBytesRead());
|
||||
readPending = false;
|
||||
|
||||
@ -598,21 +604,4 @@ public final class EpollDatagramChannel extends AbstractEpollChannel implements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need
|
||||
* to create more objects then needed.
|
||||
*/
|
||||
static final class DatagramSocketAddress extends InetSocketAddress {
|
||||
|
||||
private static final long serialVersionUID = 1348596211215015739L;
|
||||
|
||||
// holds the amount of received bytes
|
||||
final int receivedAmount;
|
||||
|
||||
DatagramSocketAddress(String addr, int port, int receivedAmount) {
|
||||
super(addr, port);
|
||||
this.receivedAmount = receivedAmount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -192,23 +192,23 @@ public final class EpollDatagramChannelConfig extends EpollChannelConfig impleme
|
||||
|
||||
@Override
|
||||
public int getSendBufferSize() {
|
||||
return Native.getSendBufferSize(datagramChannel.fd().intValue());
|
||||
return datagramChannel.fd().getSendBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setSendBufferSize(int sendBufferSize) {
|
||||
Native.setSendBufferSize(datagramChannel.fd().intValue(), sendBufferSize);
|
||||
datagramChannel.fd().setSendBufferSize(sendBufferSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() {
|
||||
return Native.getReceiveBufferSize(datagramChannel.fd().intValue());
|
||||
return datagramChannel.fd().getReceiveBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpollDatagramChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
Native.setReceiveBufferSize(datagramChannel.fd().intValue(), receiveBufferSize);
|
||||
datagramChannel.fd().setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -22,10 +22,12 @@ import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.DomainSocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.util.internal.OneTimeTask;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDomain;
|
||||
|
||||
public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel implements DomainSocketChannel {
|
||||
private final EpollDomainSocketChannelConfig config = new EpollDomainSocketChannelConfig(this);
|
||||
|
||||
@ -33,22 +35,36 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
private volatile DomainSocketAddress remote;
|
||||
|
||||
public EpollDomainSocketChannel() {
|
||||
super(Native.socketDomainFd());
|
||||
super(newSocketDomain(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollDomainSocketChannel(Channel, Socket)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollDomainSocketChannel(Channel parent, FileDescriptor fd) {
|
||||
super(parent, fd.intValue());
|
||||
super(parent, new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollDomainSocketChannel(Socket, boolean)}.
|
||||
* <p>
|
||||
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollDomainSocketChannel(FileDescriptor fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public EpollDomainSocketChannel(Channel parent, Socket fd) {
|
||||
super(parent, fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link EpollDomainSocketChannel} from an existing {@link FileDescriptor}
|
||||
*/
|
||||
public EpollDomainSocketChannel(FileDescriptor fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
EpollDomainSocketChannel(Channel parent, int fd) {
|
||||
super(parent, fd);
|
||||
public EpollDomainSocketChannel(Socket fd, boolean active) {
|
||||
super(fd, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,7 +84,7 @@ public final class EpollDomainSocketChannel extends AbstractEpollStreamChannel i
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
Native.bind(fd().intValue(), localAddress);
|
||||
fd().bind(localAddress);
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.SingleThreadEventLoop;
|
||||
import io.netty.channel.epoll.AbstractEpollChannel.AbstractEpollUnsafe;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.util.collection.IntObjectHashMap;
|
||||
import io.netty.util.collection.IntObjectMap;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
@ -48,8 +49,8 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
WAKEN_UP_UPDATER = updater;
|
||||
}
|
||||
|
||||
private final int epollFd;
|
||||
private final int eventFd;
|
||||
private final FileDescriptor epollFd;
|
||||
private final FileDescriptor eventFd;
|
||||
private final IntObjectMap<AbstractEpollChannel> channels = new IntObjectHashMap<AbstractEpollChannel>(4096);
|
||||
private final boolean allowGrowing;
|
||||
private final EpollEventArray events;
|
||||
@ -67,29 +68,29 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
events = new EpollEventArray(maxEvents);
|
||||
}
|
||||
boolean success = false;
|
||||
int epollFd = -1;
|
||||
int eventFd = -1;
|
||||
FileDescriptor epollFd = null;
|
||||
FileDescriptor eventFd = null;
|
||||
try {
|
||||
this.epollFd = epollFd = Native.epollCreate();
|
||||
this.eventFd = eventFd = Native.eventFd();
|
||||
this.epollFd = epollFd = Native.newEpollCreate();
|
||||
this.eventFd = eventFd = Native.newEventFd();
|
||||
try {
|
||||
Native.epollCtlAdd(epollFd, eventFd, Native.EPOLLIN);
|
||||
Native.epollCtlAdd(epollFd.intValue(), eventFd.intValue(), Native.EPOLLIN);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Unable to add eventFd filedescriptor to epoll", e);
|
||||
}
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
if (epollFd != -1) {
|
||||
if (epollFd != null) {
|
||||
try {
|
||||
Native.close(epollFd);
|
||||
epollFd.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
if (eventFd != -1) {
|
||||
if (eventFd != null) {
|
||||
try {
|
||||
Native.close(eventFd);
|
||||
eventFd.close();
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
@ -102,7 +103,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
protected void wakeup(boolean inEventLoop) {
|
||||
if (!inEventLoop && WAKEN_UP_UPDATER.compareAndSet(this, 0, 1)) {
|
||||
// write to the evfd which will then wake-up epoll_wait(...)
|
||||
Native.eventFdWrite(eventFd, 1L);
|
||||
Native.eventFdWrite(eventFd.intValue(), 1L);
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +113,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
void add(AbstractEpollChannel ch) throws IOException {
|
||||
assert inEventLoop();
|
||||
int fd = ch.fd().intValue();
|
||||
Native.epollCtlAdd(epollFd, fd, ch.flags);
|
||||
Native.epollCtlAdd(epollFd.intValue(), fd, ch.flags);
|
||||
channels.put(fd, ch);
|
||||
}
|
||||
|
||||
@ -121,7 +122,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
*/
|
||||
void modify(AbstractEpollChannel ch) throws IOException {
|
||||
assert inEventLoop();
|
||||
Native.epollCtlMod(epollFd, ch.fd().intValue(), ch.flags);
|
||||
Native.epollCtlMod(epollFd.intValue(), ch.fd().intValue(), ch.flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -135,7 +136,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
if (channels.remove(fd) != null) {
|
||||
// Remove the epoll. This is only needed if it's still open as otherwise it will be automatically
|
||||
// removed once the file-descriptor is closed.
|
||||
Native.epollCtlDel(epollFd, ch.fd().intValue());
|
||||
Native.epollCtlDel(epollFd.intValue(), ch.fd().intValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -172,7 +173,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
|
||||
if (timeoutMillis <= 0) {
|
||||
if (selectCnt == 0) {
|
||||
int ready = Native.epollWait(epollFd, events, 0);
|
||||
int ready = Native.epollWait(epollFd.intValue(), events, 0);
|
||||
if (ready > 0) {
|
||||
return ready;
|
||||
}
|
||||
@ -180,7 +181,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
break;
|
||||
}
|
||||
|
||||
int selectedKeys = Native.epollWait(epollFd, events, (int) timeoutMillis);
|
||||
int selectedKeys = Native.epollWait(epollFd.intValue(), events, (int) timeoutMillis);
|
||||
selectCnt ++;
|
||||
|
||||
if (selectedKeys != 0 || oldWakenUp || wakenUp == 1 || hasTasks() || hasScheduledTasks()) {
|
||||
@ -203,7 +204,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
int ready;
|
||||
if (hasTasks()) {
|
||||
// Non blocking just return what is ready directly without block
|
||||
ready = Native.epollWait(epollFd, events, 0);
|
||||
ready = Native.epollWait(epollFd.intValue(), events, 0);
|
||||
} else {
|
||||
ready = epollWait(oldWakenUp);
|
||||
|
||||
@ -236,7 +237,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
// (OK - no wake-up required).
|
||||
|
||||
if (wakenUp == 1) {
|
||||
Native.eventFdWrite(eventFd, 1L);
|
||||
Native.eventFdWrite(eventFd.intValue(), 1L);
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,7 +283,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
|
||||
private void closeAll() {
|
||||
try {
|
||||
Native.epollWait(epollFd, events, 0);
|
||||
Native.epollWait(epollFd.intValue(), events, 0);
|
||||
} catch (IOException ignore) {
|
||||
// ignore on close
|
||||
}
|
||||
@ -300,9 +301,9 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
private void processReady(EpollEventArray events, int ready) {
|
||||
for (int i = 0; i < ready; i ++) {
|
||||
final int fd = events.fd(i);
|
||||
if (fd == eventFd) {
|
||||
if (fd == eventFd.intValue()) {
|
||||
// consume wakeup event
|
||||
Native.eventFdRead(eventFd);
|
||||
Native.eventFdRead(eventFd.intValue());
|
||||
} else {
|
||||
final long ev = events.events(i);
|
||||
|
||||
@ -346,7 +347,7 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
} else {
|
||||
// We received an event for an fd which we not use anymore. Remove it from the epoll_event set.
|
||||
try {
|
||||
Native.epollCtlDel(epollFd, fd);
|
||||
Native.epollCtlDel(epollFd.intValue(), fd);
|
||||
} catch (IOException ignore) {
|
||||
// This can happen but is nothing we need to worry about as we only try to delete
|
||||
// the fd from the epoll set as we not found it in our mappings. So this call to
|
||||
@ -362,12 +363,12 @@ final class EpollEventLoop extends SingleThreadEventLoop {
|
||||
protected void cleanup() {
|
||||
try {
|
||||
try {
|
||||
Native.close(epollFd);
|
||||
epollFd.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to close the epoll fd.", e);
|
||||
}
|
||||
try {
|
||||
Native.close(eventFd);
|
||||
eventFd.close();
|
||||
} catch (IOException e) {
|
||||
logger.warn("Failed to close the event fd.", e);
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import io.netty.buffer.ByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.MessageSizeEstimator;
|
||||
import io.netty.channel.RecvByteBufAllocator;
|
||||
@ -96,11 +95,11 @@ public class EpollServerChannelConfig extends EpollChannelConfig {
|
||||
}
|
||||
|
||||
public int getReceiveBufferSize() {
|
||||
return Native.getReceiveBufferSize(channel.fd().intValue());
|
||||
return channel.fd().getReceiveBufferSize();
|
||||
}
|
||||
|
||||
public EpollServerChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
Native.setReceiveBufferSize(channel.fd().intValue(), receiveBufferSize);
|
||||
channel.fd().setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -19,12 +19,15 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.ServerDomainSocketChannel;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.internal.logging.InternalLogger;
|
||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.SocketAddress;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketDomain;
|
||||
|
||||
|
||||
public final class EpollServerDomainSocketChannel extends AbstractEpollServerChannel
|
||||
implements ServerDomainSocketChannel {
|
||||
@ -35,19 +38,31 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
|
||||
private volatile DomainSocketAddress local;
|
||||
|
||||
public EpollServerDomainSocketChannel() {
|
||||
super(Native.socketDomainFd());
|
||||
super(newSocketDomain(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
|
||||
* Creates a new {@link EpollServerDomainSocketChannel} from an existing {@link FileDescriptor}.
|
||||
*/
|
||||
public EpollServerDomainSocketChannel(FileDescriptor fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerDomainSocketChannel(Socket, boolean)}.
|
||||
*/
|
||||
public EpollServerDomainSocketChannel(Socket fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public EpollServerDomainSocketChannel(Socket fd, boolean active) {
|
||||
super(fd, active);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Channel newChildChannel(int fd, byte[] addr, int offset, int len) throws Exception {
|
||||
return new EpollDomainSocketChannel(this, fd);
|
||||
return new EpollDomainSocketChannel(this, new Socket(fd));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -57,9 +72,8 @@ public final class EpollServerDomainSocketChannel extends AbstractEpollServerCha
|
||||
|
||||
@Override
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
int fd = fd().intValue();
|
||||
Native.bind(fd, localAddress);
|
||||
Native.listen(fd, config.getBacklog());
|
||||
fd().bind(localAddress);
|
||||
fd().listen(config.getBacklog());
|
||||
local = (DomainSocketAddress) localAddress;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ import io.netty.channel.Channel;
|
||||
import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@ -27,6 +28,9 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.channel.unix.NativeInetAddress.address;
|
||||
import static io.netty.channel.unix.Socket.newSocketStream;
|
||||
|
||||
/**
|
||||
* {@link ServerSocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
|
||||
* maximal performance.
|
||||
@ -38,20 +42,40 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
|
||||
|
||||
public EpollServerSocketChannel() {
|
||||
super(Native.socketStreamFd());
|
||||
super(newSocketStream(), false);
|
||||
config = new EpollServerSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
|
||||
* Creates a new {@link EpollServerSocketChannel} from an existing {@link FileDescriptor}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollServerSocketChannel(FileDescriptor fd) {
|
||||
super(fd);
|
||||
config = new EpollServerSocketChannelConfig(this);
|
||||
// Must call this constructor to ensure this object's local address is configured correctly.
|
||||
// The local address can only be obtained from a Socket object.
|
||||
this(new Socket(fd.intValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollServerSocketChannel(Socket, boolean)}.
|
||||
* Creates a new {@link EpollServerSocketChannel} from an existing {@link Socket}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollServerSocketChannel(Socket fd) {
|
||||
super(fd);
|
||||
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
local = Native.localAddress(fd.intValue());
|
||||
local = fd.localAddress();
|
||||
config = new EpollServerSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
public EpollServerSocketChannel(Socket fd, boolean active) {
|
||||
super(fd, active);
|
||||
// As we create an EpollServerSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound already.
|
||||
local = fd.localAddress();
|
||||
config = new EpollServerSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,13 +87,12 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
protected void doBind(SocketAddress localAddress) throws Exception {
|
||||
InetSocketAddress addr = (InetSocketAddress) localAddress;
|
||||
checkResolvable(addr);
|
||||
int fd = fd().intValue();
|
||||
Native.bind(fd, addr);
|
||||
local = Native.localAddress(fd);
|
||||
fd().bind(addr);
|
||||
local = fd().localAddress();
|
||||
if (Native.IS_SUPPORTING_TCP_FASTOPEN && config.getTcpFastopen() > 0) {
|
||||
Native.setTcpFastopen(fd, config.getTcpFastopen());
|
||||
Native.setTcpFastopen(fd().intValue(), config.getTcpFastopen());
|
||||
}
|
||||
Native.listen(fd, config.getBacklog());
|
||||
fd().listen(config.getBacklog());
|
||||
active = true;
|
||||
}
|
||||
|
||||
@ -95,7 +118,7 @@ public final class EpollServerSocketChannel extends AbstractEpollServerChannel i
|
||||
|
||||
@Override
|
||||
protected Channel newChildChannel(int fd, byte[] address, int offset, int len) throws Exception {
|
||||
return new EpollSocketChannel(this, fd, Native.address(address, offset, len));
|
||||
return new EpollSocketChannel(this, new Socket(fd), address(address, offset, len));
|
||||
}
|
||||
|
||||
Collection<InetAddress> tcpMd5SigAddresses() {
|
||||
|
@ -22,6 +22,7 @@ import io.netty.channel.EventLoop;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.Socket;
|
||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||
import io.netty.util.internal.OneTimeTask;
|
||||
|
||||
@ -33,6 +34,8 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static io.netty.channel.unix.Socket.newSocketStream;
|
||||
|
||||
/**
|
||||
* {@link SocketChannel} implementation that uses linux EPOLL Edge-Triggered Mode for
|
||||
* maximal performance.
|
||||
@ -45,13 +48,13 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
private volatile InetSocketAddress remote;
|
||||
private volatile Collection<InetAddress> tcpMd5SigAddresses = Collections.emptyList();
|
||||
|
||||
EpollSocketChannel(Channel parent, int fd, InetSocketAddress remote) {
|
||||
EpollSocketChannel(Channel parent, Socket fd, InetSocketAddress remote) {
|
||||
super(parent, fd);
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
// Directly cache the remote and local addresses
|
||||
// See https://github.com/netty/netty/issues/2359
|
||||
this.remote = remote;
|
||||
local = Native.localAddress(fd);
|
||||
local = fd.localAddress();
|
||||
|
||||
if (parent instanceof EpollServerSocketChannel) {
|
||||
tcpMd5SigAddresses = ((EpollServerSocketChannel) parent).tcpMd5SigAddresses();
|
||||
@ -59,21 +62,33 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
}
|
||||
|
||||
public EpollSocketChannel() {
|
||||
super(Native.socketStreamFd());
|
||||
super(newSocketStream(), false);
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #EpollSocketChannel(Socket, boolean)}.
|
||||
*/
|
||||
@Deprecated
|
||||
public EpollSocketChannel(FileDescriptor fd) {
|
||||
super(fd);
|
||||
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound/connected already.
|
||||
remote = fd().remoteAddress();
|
||||
local = fd().localAddress();
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link EpollSocketChannel} from an existing {@link FileDescriptor}.
|
||||
*/
|
||||
public EpollSocketChannel(FileDescriptor fd) {
|
||||
super(fd);
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
|
||||
public EpollSocketChannel(Socket fd, boolean active) {
|
||||
super(fd, active);
|
||||
// As we create an EpollSocketChannel from a FileDescriptor we should try to obtain the remote and local
|
||||
// address from it. This is needed as the FileDescriptor may be bound/connected already.
|
||||
remote = Native.remoteAddress(fd.intValue());
|
||||
local = Native.localAddress(fd.intValue());
|
||||
remote = fd.remoteAddress();
|
||||
local = fd.localAddress();
|
||||
config = new EpollSocketChannelConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -111,7 +126,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
protected SocketAddress remoteAddress0() {
|
||||
if (remote == null) {
|
||||
// Remote address not know, try to get it now.
|
||||
InetSocketAddress address = Native.remoteAddress(fd().intValue());
|
||||
InetSocketAddress address = fd().remoteAddress();
|
||||
if (address != null) {
|
||||
remote = address;
|
||||
}
|
||||
@ -123,9 +138,8 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
@Override
|
||||
protected void doBind(SocketAddress local) throws Exception {
|
||||
InetSocketAddress localAddress = (InetSocketAddress) local;
|
||||
int fd = fd().intValue();
|
||||
Native.bind(fd, localAddress);
|
||||
this.local = Native.localAddress(fd);
|
||||
fd().bind(localAddress);
|
||||
this.local = fd().localAddress();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -190,7 +204,6 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
checkResolvable((InetSocketAddress) localAddress);
|
||||
}
|
||||
checkResolvable((InetSocketAddress) remoteAddress);
|
||||
int fd = fd().intValue();
|
||||
boolean connected = super.doConnect(remoteAddress, localAddress);
|
||||
if (connected) {
|
||||
remote = (InetSocketAddress) remoteAddress;
|
||||
@ -199,7 +212,7 @@ public final class EpollSocketChannel extends AbstractEpollStreamChannel impleme
|
||||
// We always need to set the localAddress even if not connected yet
|
||||
//
|
||||
// See https://github.com/netty/netty/issues/3463
|
||||
local = Native.localAddress(fd);
|
||||
local = fd().localAddress();
|
||||
return connected;
|
||||
}
|
||||
|
||||
|
@ -147,17 +147,17 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
|
||||
@Override
|
||||
public int getReceiveBufferSize() {
|
||||
return Native.getReceiveBufferSize(channel.fd().intValue());
|
||||
return channel.fd().getReceiveBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSendBufferSize() {
|
||||
return Native.getSendBufferSize(channel.fd().intValue());
|
||||
return channel.fd().getSendBufferSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSoLinger() {
|
||||
return Native.getSoLinger(channel.fd().intValue());
|
||||
return channel.fd().getSoLinger();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,7 +167,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
|
||||
@Override
|
||||
public boolean isKeepAlive() {
|
||||
return Native.isKeepAlive(channel.fd().intValue()) == 1;
|
||||
return channel.fd().isKeepAlive();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -177,14 +177,14 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
|
||||
@Override
|
||||
public boolean isTcpNoDelay() {
|
||||
return Native.isTcpNoDelay(channel.fd().intValue()) == 1;
|
||||
return channel.fd().isTcpNoDelay();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
|
||||
*/
|
||||
public boolean isTcpCork() {
|
||||
return Native.isTcpCork(channel.fd().intValue()) == 1;
|
||||
return channel.fd().isTcpCork();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -225,7 +225,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
|
||||
@Override
|
||||
public EpollSocketChannelConfig setKeepAlive(boolean keepAlive) {
|
||||
Native.setKeepAlive(channel.fd().intValue(), keepAlive ? 1 : 0);
|
||||
channel.fd().setKeepAlive(keepAlive);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -237,7 +237,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
|
||||
@Override
|
||||
public EpollSocketChannelConfig setReceiveBufferSize(int receiveBufferSize) {
|
||||
Native.setReceiveBufferSize(channel.fd().intValue(), receiveBufferSize);
|
||||
channel.fd().setReceiveBufferSize(receiveBufferSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -249,19 +249,19 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
|
||||
@Override
|
||||
public EpollSocketChannelConfig setSendBufferSize(int sendBufferSize) {
|
||||
Native.setSendBufferSize(channel.fd().intValue(), sendBufferSize);
|
||||
channel.fd().setSendBufferSize(sendBufferSize);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpollSocketChannelConfig setSoLinger(int soLinger) {
|
||||
Native.setSoLinger(channel.fd().intValue(), soLinger);
|
||||
channel.fd().setSoLinger(soLinger);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EpollSocketChannelConfig setTcpNoDelay(boolean tcpNoDelay) {
|
||||
Native.setTcpNoDelay(channel.fd().intValue(), tcpNoDelay ? 1 : 0);
|
||||
channel.fd().setTcpNoDelay(tcpNoDelay);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -269,7 +269,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
|
||||
* Set the {@code TCP_CORK} option on the socket. See {@code man 7 tcp} for more details.
|
||||
*/
|
||||
public EpollSocketChannelConfig setTcpCork(boolean tcpCork) {
|
||||
Native.setTcpCork(channel.fd().intValue(), tcpCork ? 1 : 0);
|
||||
channel.fd().setTcpCork(tcpCork);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -15,34 +15,31 @@
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.channel.DefaultFileRegion;
|
||||
import io.netty.channel.unix.DomainSocketAddress;
|
||||
import io.netty.util.CharsetUtil;
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
import io.netty.channel.unix.Errors.NativeIoException;
|
||||
import io.netty.channel.unix.FileDescriptor;
|
||||
import io.netty.channel.unix.NativeInetAddress;
|
||||
import io.netty.util.internal.NativeLibraryLoader;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Locale;
|
||||
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EPIPE_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ioResult;
|
||||
import static io.netty.channel.unix.Errors.newConnectionResetException;
|
||||
import static io.netty.channel.unix.Errors.newIOException;
|
||||
|
||||
/**
|
||||
* Native helper methods
|
||||
*
|
||||
* <strong>Internal usage only!</strong>
|
||||
*/
|
||||
public final class Native {
|
||||
|
||||
static {
|
||||
String name = SystemPropertyUtil.get("os.name").toLowerCase(Locale.UK).trim();
|
||||
if (!name.startsWith("linux")) {
|
||||
@ -65,95 +62,33 @@ public final class Native {
|
||||
public static final long SSIZE_MAX = ssizeMax();
|
||||
public static final int TCP_MD5SIG_MAXKEYLEN = tcpMd5SigMaxKeyLen();
|
||||
|
||||
private static final byte[] IPV4_MAPPED_IPV6_PREFIX = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff };
|
||||
|
||||
// As all our JNI methods return -errno on error we need to compare with the negative errno codes.
|
||||
private static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
|
||||
private static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
|
||||
private static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
|
||||
private static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
|
||||
private static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
|
||||
private static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
|
||||
private static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
|
||||
|
||||
/**
|
||||
* Holds the mappings for errno codes to String messages.
|
||||
* This eliminates the need to call back into JNI to get the right String message on an exception
|
||||
* and thus is faster.
|
||||
*
|
||||
* The array length of 1024 should be more then enough because errno.h only holds < 200 codes.
|
||||
*/
|
||||
private static final String[] ERRORS = new String[1024];
|
||||
|
||||
// Pre-instantiated exceptions which does not need any stacktrace and
|
||||
// can be thrown multiple times for performance reasons.
|
||||
private static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_WRITE;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_WRITEV;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_READ;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_SENDFILE;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_SENDTO;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_SENDMSG;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_SENDMMSG;
|
||||
private static final IOException CONNECTION_RESET_EXCEPTION_SPLICE;
|
||||
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDFILE;
|
||||
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMMSG;
|
||||
private static final NativeIoException CONNECTION_RESET_EXCEPTION_SPLICE;
|
||||
|
||||
static {
|
||||
for (int i = 0; i < ERRORS.length; i++) {
|
||||
// This is ok as strerror returns 'Unknown error i' when the message is not known.
|
||||
ERRORS[i] = strError(i);
|
||||
}
|
||||
|
||||
CONNECTION_RESET_EXCEPTION_READ = newConnectionResetException("syscall:read(...)",
|
||||
ERRNO_ECONNRESET_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SENDFILE = newConnectionResetException("syscall:sendfile(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SENDMMSG = newConnectionResetException("syscall:sendmmsg(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SPLICE = newConnectionResetException("syscall:splice(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();
|
||||
CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
||||
}
|
||||
|
||||
private static IOException newConnectionResetException(String method, int errnoNegative) {
|
||||
IOException exception = newIOException(method, errnoNegative);
|
||||
exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
||||
return exception;
|
||||
public static FileDescriptor newEventFd() {
|
||||
return new FileDescriptor(eventFd());
|
||||
}
|
||||
|
||||
public static IOException newIOException(String method, int err) {
|
||||
return new IOException(method + "() failed: " + ERRORS[-err]);
|
||||
}
|
||||
|
||||
private static int ioResult(String method, int err, IOException resetCause) throws IOException {
|
||||
// network stack saturated... try again later
|
||||
if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
|
||||
return 0;
|
||||
}
|
||||
if (err == ERRNO_EPIPE_NEGATIVE || err == ERRNO_ECONNRESET_NEGATIVE) {
|
||||
throw resetCause;
|
||||
}
|
||||
if (err == ERRNO_EBADF_NEGATIVE || err == ERRNO_ENOTCONN_NEGATIVE) {
|
||||
throw CLOSED_CHANNEL_EXCEPTION;
|
||||
}
|
||||
// TODO: We could even go futher and use a pre-instanced IOException for the other error codes, but for
|
||||
// all other errors it may be better to just include a stacktrace.
|
||||
throw newIOException(method, err);
|
||||
}
|
||||
|
||||
public static native int eventFd();
|
||||
private static native int eventFd();
|
||||
public static native void eventFdWrite(int fd, long value);
|
||||
public static native void eventFdRead(int fd);
|
||||
public static native int epollCreate();
|
||||
|
||||
public static FileDescriptor newEpollCreate() {
|
||||
return new FileDescriptor(epollCreate());
|
||||
}
|
||||
|
||||
private static native int epollCreate();
|
||||
|
||||
public static int epollWait(int efd, EpollEventArray events, int timeout) throws IOException {
|
||||
int ready = epollWait0(efd, events.memoryAddress(), events.length(), timeout);
|
||||
if (ready < 0) {
|
||||
@ -187,26 +122,7 @@ public final class Native {
|
||||
}
|
||||
private static native int epollCtlDel0(int efd, final int fd);
|
||||
|
||||
private static native int errnoEBADF();
|
||||
private static native int errnoEPIPE();
|
||||
private static native int errnoECONNRESET();
|
||||
private static native int errnoENOTCONN();
|
||||
|
||||
private static native int errnoEAGAIN();
|
||||
private static native int errnoEWOULDBLOCK();
|
||||
private static native int errnoEINPROGRESS();
|
||||
private static native String strError(int err);
|
||||
|
||||
// File-descriptor operations
|
||||
public static void close(int fd) throws IOException {
|
||||
int res = close0(fd);
|
||||
if (res < 0) {
|
||||
throw newIOException("close", res);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int close0(int fd);
|
||||
|
||||
public static int splice(int fd, long offIn, int fdOut, long offOut, long len) throws IOException {
|
||||
int res = splice0(fd, offIn, fdOut, offOut, len);
|
||||
if (res >= 0) {
|
||||
@ -217,83 +133,6 @@ public final class Native {
|
||||
|
||||
private static native int splice0(int fd, long offIn, int fdOut, long offOut, long len);
|
||||
|
||||
public static long pipe() throws IOException {
|
||||
long res = pipe0();
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
throw newIOException("pipe", (int) res);
|
||||
}
|
||||
|
||||
private static native long pipe0();
|
||||
|
||||
public static int write(int fd, ByteBuffer buf, int pos, int limit) throws IOException {
|
||||
int res = write0(fd, buf, pos, limit);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE);
|
||||
}
|
||||
|
||||
private static native int write0(int fd, ByteBuffer buf, int pos, int limit);
|
||||
|
||||
public static int writeAddress(int fd, long address, int pos, int limit) throws IOException {
|
||||
int res = writeAddress0(fd, address, pos, limit);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("writeAddress", res, CONNECTION_RESET_EXCEPTION_WRITE);
|
||||
}
|
||||
|
||||
private static native int writeAddress0(int fd, long address, int pos, int limit);
|
||||
|
||||
public static long writev(int fd, ByteBuffer[] buffers, int offset, int length) throws IOException {
|
||||
long res = writev0(fd, buffers, offset, length);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
|
||||
}
|
||||
|
||||
private static native long writev0(int fd, ByteBuffer[] buffers, int offset, int length);
|
||||
|
||||
public static long writevAddresses(int fd, long memoryAddress, int length)
|
||||
throws IOException {
|
||||
long res = writevAddresses0(fd, memoryAddress, length);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("writevAddresses", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
|
||||
}
|
||||
|
||||
private static native long writevAddresses0(int fd, long memoryAddress, int length);
|
||||
|
||||
public static int read(int fd, ByteBuffer buf, int pos, int limit) throws IOException {
|
||||
int res = read0(fd, buf, pos, limit);
|
||||
if (res > 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) {
|
||||
return -1;
|
||||
}
|
||||
return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ);
|
||||
}
|
||||
|
||||
private static native int read0(int fd, ByteBuffer buf, int pos, int limit);
|
||||
|
||||
public static int readAddress(int fd, long address, int pos, int limit) throws IOException {
|
||||
int res = readAddress0(fd, address, pos, limit);
|
||||
if (res > 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) {
|
||||
return -1;
|
||||
}
|
||||
return ioResult("readAddress", res, CONNECTION_RESET_EXCEPTION_READ);
|
||||
}
|
||||
|
||||
private static native int readAddress0(int fd, long address, int pos, int limit);
|
||||
|
||||
public static long sendfile(
|
||||
int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException {
|
||||
// Open the file-region as it may be created via the lazy constructor. This is needed as we directly access
|
||||
@ -310,84 +149,6 @@ public final class Native {
|
||||
private static native long sendfile0(
|
||||
int dest, DefaultFileRegion src, long baseOffset, long offset, long length) throws IOException;
|
||||
|
||||
public static int sendTo(
|
||||
int fd, ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
|
||||
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
|
||||
// to be called frequently
|
||||
byte[] address;
|
||||
int scopeId;
|
||||
if (addr instanceof Inet6Address) {
|
||||
address = addr.getAddress();
|
||||
scopeId = ((Inet6Address) addr).getScopeId();
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
scopeId = 0;
|
||||
address = ipv4MappedIpv6Address(addr.getAddress());
|
||||
}
|
||||
int res = sendTo0(fd, buf, pos, limit, address, scopeId, port);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO);
|
||||
}
|
||||
|
||||
private static native int sendTo0(
|
||||
int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port);
|
||||
|
||||
public static int sendToAddress(
|
||||
int fd, long memoryAddress, int pos, int limit, InetAddress addr, int port) throws IOException {
|
||||
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
|
||||
// to be called frequently
|
||||
byte[] address;
|
||||
int scopeId;
|
||||
if (addr instanceof Inet6Address) {
|
||||
address = addr.getAddress();
|
||||
scopeId = ((Inet6Address) addr).getScopeId();
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
scopeId = 0;
|
||||
address = ipv4MappedIpv6Address(addr.getAddress());
|
||||
}
|
||||
int res = sendToAddress0(fd, memoryAddress, pos, limit, address, scopeId, port);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO);
|
||||
}
|
||||
|
||||
private static native int sendToAddress0(
|
||||
int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port);
|
||||
|
||||
public static int sendToAddresses(
|
||||
int fd, long memoryAddress, int length, InetAddress addr, int port) throws IOException {
|
||||
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
|
||||
// to be called frequently
|
||||
byte[] address;
|
||||
int scopeId;
|
||||
if (addr instanceof Inet6Address) {
|
||||
address = addr.getAddress();
|
||||
scopeId = ((Inet6Address) addr).getScopeId();
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
scopeId = 0;
|
||||
address = ipv4MappedIpv6Address(addr.getAddress());
|
||||
}
|
||||
int res = sendToAddresses(fd, memoryAddress, length, address, scopeId, port);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG);
|
||||
}
|
||||
|
||||
private static native int sendToAddresses(
|
||||
int fd, long memoryAddress, int length, byte[] address, int scopeId, int port);
|
||||
|
||||
public static native EpollDatagramChannel.DatagramSocketAddress recvFrom(
|
||||
int fd, ByteBuffer buf, int pos, int limit) throws IOException;
|
||||
|
||||
public static native EpollDatagramChannel.DatagramSocketAddress recvFromAddress(
|
||||
int fd, long memoryAddress, int pos, int limit) throws IOException;
|
||||
|
||||
public static int sendmmsg(
|
||||
int fd, NativeDatagramPacketArray.NativeDatagramPacket[] msgs, int offset, int len) throws IOException {
|
||||
int res = sendmmsg0(fd, msgs, offset, len);
|
||||
@ -403,188 +164,6 @@ public final class Native {
|
||||
private static native boolean isSupportingSendmmsg();
|
||||
private static native boolean isSupportingTcpFastopen();
|
||||
|
||||
// socket operations
|
||||
public static int socketStreamFd() {
|
||||
int res = socketStream();
|
||||
if (res < 0) {
|
||||
throw new ChannelException(newIOException("socketStreamFd", res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static int socketDgramFd() {
|
||||
int res = socketDgram();
|
||||
if (res < 0) {
|
||||
throw new ChannelException(newIOException("socketDgramFd", res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public static int socketDomainFd() {
|
||||
int res = socketDomain();
|
||||
if (res < 0) {
|
||||
throw new ChannelException(newIOException("socketDomain", res));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static native int socketStream();
|
||||
private static native int socketDgram();
|
||||
private static native int socketDomain();
|
||||
|
||||
public static void bind(int fd, SocketAddress socketAddress) throws IOException {
|
||||
if (socketAddress instanceof InetSocketAddress) {
|
||||
InetSocketAddress addr = (InetSocketAddress) socketAddress;
|
||||
NativeInetAddress address = toNativeInetAddress(addr.getAddress());
|
||||
int res = bind(fd, address.address, address.scopeId, addr.getPort());
|
||||
if (res < 0) {
|
||||
throw newIOException("bind", res);
|
||||
}
|
||||
} else if (socketAddress instanceof DomainSocketAddress) {
|
||||
DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
|
||||
int res = bindDomainSocket(fd, addr.path().getBytes(CharsetUtil.UTF_8));
|
||||
if (res < 0) {
|
||||
throw newIOException("bind", res);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int bind(int fd, byte[] address, int scopeId, int port);
|
||||
private static native int bindDomainSocket(int fd, byte[] path);
|
||||
|
||||
public static void listen(int fd, int backlog) throws IOException {
|
||||
int res = listen0(fd, backlog);
|
||||
if (res < 0) {
|
||||
throw newIOException("listen", res);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int listen0(int fd, int backlog);
|
||||
|
||||
public static boolean connect(int fd, SocketAddress socketAddress) throws IOException {
|
||||
int res;
|
||||
if (socketAddress instanceof InetSocketAddress) {
|
||||
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
|
||||
NativeInetAddress address = toNativeInetAddress(inetSocketAddress.getAddress());
|
||||
res = connect(fd, address.address, address.scopeId, inetSocketAddress.getPort());
|
||||
} else if (socketAddress instanceof DomainSocketAddress) {
|
||||
DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
|
||||
res = connectDomainSocket(fd, unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8));
|
||||
} else {
|
||||
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
|
||||
}
|
||||
if (res < 0) {
|
||||
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
|
||||
// connect not complete yet need to wait for EPOLLOUT event
|
||||
return false;
|
||||
}
|
||||
throw newConnectException("connect", res);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static native int connect(int fd, byte[] address, int scopeId, int port);
|
||||
private static native int connectDomainSocket(int fd, byte[] path);
|
||||
|
||||
public static boolean finishConnect(int fd) throws IOException {
|
||||
int res = finishConnect0(fd);
|
||||
if (res < 0) {
|
||||
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
|
||||
// connect still in progress
|
||||
return false;
|
||||
}
|
||||
throw newConnectException("finishConnect", res);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static native int finishConnect0(int fd);
|
||||
|
||||
private static ConnectException newConnectException(String method, int err) {
|
||||
return new ConnectException(method + "() failed: " + ERRORS[-err]);
|
||||
}
|
||||
|
||||
public static InetSocketAddress remoteAddress(int fd) {
|
||||
byte[] addr = remoteAddress0(fd);
|
||||
// addr may be null if getpeername failed.
|
||||
// See https://github.com/netty/netty/issues/3328
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
return address(addr, 0, addr.length);
|
||||
}
|
||||
|
||||
public static InetSocketAddress localAddress(int fd) {
|
||||
byte[] addr = localAddress0(fd);
|
||||
// addr may be null if getpeername failed.
|
||||
// See https://github.com/netty/netty/issues/3328
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
return address(addr, 0, addr.length);
|
||||
}
|
||||
|
||||
static InetSocketAddress address(byte[] addr, int offset, int len) {
|
||||
// The last 4 bytes are always the port
|
||||
final int port = decodeInt(addr, offset + len - 4);
|
||||
final InetAddress address;
|
||||
|
||||
try {
|
||||
switch (len) {
|
||||
// 8 bytes:
|
||||
// - 4 == ipaddress
|
||||
// - 4 == port
|
||||
case 8:
|
||||
byte[] ipv4 = new byte[4];
|
||||
System.arraycopy(addr, offset, ipv4, 0, 4);
|
||||
address = InetAddress.getByAddress(ipv4);
|
||||
break;
|
||||
|
||||
// 24 bytes:
|
||||
// - 16 == ipaddress
|
||||
// - 4 == scopeId
|
||||
// - 4 == port
|
||||
case 24:
|
||||
byte[] ipv6 = new byte[16];
|
||||
System.arraycopy(addr, offset, ipv6, 0, 16);
|
||||
int scopeId = decodeInt(addr, offset + len - 8);
|
||||
address = Inet6Address.getByAddress(null, ipv6, scopeId);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
return new InetSocketAddress(address, port);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new Error("Should never happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
static int decodeInt(byte[] addr, int index) {
|
||||
return (addr[index] & 0xff) << 24 |
|
||||
(addr[index + 1] & 0xff) << 16 |
|
||||
(addr[index + 2] & 0xff) << 8 |
|
||||
addr[index + 3] & 0xff;
|
||||
}
|
||||
|
||||
private static native byte[] remoteAddress0(int fd);
|
||||
private static native byte[] localAddress0(int fd);
|
||||
|
||||
public static int accept(int fd, byte[] addr) throws IOException {
|
||||
int res = accept0(fd, addr);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
|
||||
// Everything consumed so just return -1 here.
|
||||
return -1;
|
||||
}
|
||||
throw newIOException("accept", res);
|
||||
}
|
||||
|
||||
private static native int accept0(int fd, byte[] addr);
|
||||
|
||||
public static int recvFd(int fd) throws IOException {
|
||||
int res = recvFd0(fd);
|
||||
if (res > 0) {
|
||||
@ -617,44 +196,22 @@ public final class Native {
|
||||
|
||||
private static native int sendFd0(int socketFd, int fd);
|
||||
|
||||
public static void shutdown(int fd, boolean read, boolean write) throws IOException {
|
||||
int res = shutdown0(fd, read, write);
|
||||
if (res < 0) {
|
||||
throw newIOException("shutdown", res);
|
||||
}
|
||||
}
|
||||
|
||||
private static native int shutdown0(int fd, boolean read, boolean write);
|
||||
|
||||
// Socket option operations
|
||||
public static native int getReceiveBufferSize(int fd);
|
||||
public static native int getSendBufferSize(int fd);
|
||||
public static native int isKeepAlive(int fd);
|
||||
public static native int isReuseAddress(int fd);
|
||||
public static native int isReusePort(int fd);
|
||||
public static native int isTcpNoDelay(int fd);
|
||||
public static native int isTcpCork(int fd);
|
||||
public static native int getTcpNotSentLowAt(int fd);
|
||||
public static native int getSoLinger(int fd);
|
||||
public static native int getTrafficClass(int fd);
|
||||
public static native int isBroadcast(int fd);
|
||||
public static native int getTcpKeepIdle(int fd);
|
||||
public static native int getTcpKeepIntvl(int fd);
|
||||
public static native int getTcpKeepCnt(int fd);
|
||||
public static native int getTcpUserTimeout(int milliseconds);
|
||||
public static native int getSoError(int fd);
|
||||
public static native int isIpFreeBind(int fd);
|
||||
|
||||
public static native void setKeepAlive(int fd, int keepAlive);
|
||||
public static native void setReceiveBufferSize(int fd, int receiveBufferSize);
|
||||
public static native void setReuseAddress(int fd, int reuseAddress);
|
||||
public static native void setReusePort(int fd, int reuseAddress);
|
||||
public static native void setSendBufferSize(int fd, int sendBufferSize);
|
||||
public static native void setTcpNoDelay(int fd, int tcpNoDelay);
|
||||
public static native void setTcpCork(int fd, int tcpCork);
|
||||
public static native void setTcpFastopen(int fd, int tcpFastopenBacklog);
|
||||
public static native void setTcpNotSentLowAt(int fd, int tcpNotSentLowAt);
|
||||
public static native void setSoLinger(int fd, int soLinger);
|
||||
public static native void setTrafficClass(int fd, int tcpNoDelay);
|
||||
public static native void setBroadcast(int fd, int broadcast);
|
||||
public static native void setTcpKeepIdle(int fd, int seconds);
|
||||
@ -669,43 +226,12 @@ public final class Native {
|
||||
private static native void tcpInfo0(int fd, int[] array);
|
||||
|
||||
public static void setTcpMd5Sig(int fd, InetAddress address, byte[] key) {
|
||||
final NativeInetAddress a = toNativeInetAddress(address);
|
||||
setTcpMd5Sig0(fd, a.address, a.scopeId, key);
|
||||
final NativeInetAddress a = NativeInetAddress.newInstance(address);
|
||||
setTcpMd5Sig0(fd, a.address(), a.scopeId(), key);
|
||||
}
|
||||
|
||||
private static native void setTcpMd5Sig0(int fd, byte[] address, int scopeId, byte[] key);
|
||||
|
||||
private static NativeInetAddress toNativeInetAddress(InetAddress addr) {
|
||||
byte[] bytes = addr.getAddress();
|
||||
if (addr instanceof Inet6Address) {
|
||||
return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId());
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
return new NativeInetAddress(ipv4MappedIpv6Address(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] ipv4MappedIpv6Address(byte[] ipv4) {
|
||||
byte[] address = new byte[16];
|
||||
System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length);
|
||||
System.arraycopy(ipv4, 0, address, 12, ipv4.length);
|
||||
return address;
|
||||
}
|
||||
|
||||
private static class NativeInetAddress {
|
||||
final byte[] address;
|
||||
final int scopeId;
|
||||
|
||||
NativeInetAddress(byte[] address, int scopeId) {
|
||||
this.address = address;
|
||||
this.scopeId = scopeId;
|
||||
}
|
||||
|
||||
NativeInetAddress(byte[] address) {
|
||||
this(address, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static native String kernelVersion();
|
||||
|
||||
private static native int iovMax();
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package io.netty.channel.epoll;
|
||||
|
||||
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelOutboundBuffer;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
@ -148,7 +149,7 @@ final class NativeDatagramPacketArray implements ChannelOutboundBuffer.MessagePr
|
||||
addr = address.getAddress();
|
||||
scopeId = ((Inet6Address) address).getScopeId();
|
||||
} else {
|
||||
addr = Native.ipv4MappedIpv6Address(address.getAddress());
|
||||
addr = ipv4MappedIpv6Address(address.getAddress());
|
||||
scopeId = 0;
|
||||
}
|
||||
port = recipient.getPort();
|
||||
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
package io.netty.channel.unix;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* Act as special {@link InetSocketAddress} to be able to easily pass all needed data from JNI without the need
|
||||
* to create more objects then needed.
|
||||
* <p>
|
||||
* <strong>Internal usage only!</strong>
|
||||
*/
|
||||
public final class DatagramSocketAddress extends InetSocketAddress {
|
||||
private static final long serialVersionUID = 3094819287843178401L;
|
||||
|
||||
// holds the amount of received bytes
|
||||
private final int receivedAmount;
|
||||
|
||||
DatagramSocketAddress(String addr, int port, int receivedAmount) {
|
||||
super(addr, port);
|
||||
this.receivedAmount = receivedAmount;
|
||||
}
|
||||
|
||||
public int receivedAmount() {
|
||||
return receivedAmount;
|
||||
}
|
||||
}
|
@ -0,0 +1,134 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
package io.netty.channel.unix;
|
||||
|
||||
import io.netty.util.internal.EmptyArrays;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
|
||||
/**
|
||||
* <strong>Internal usage only!</strong>
|
||||
*/
|
||||
public final class Errors {
|
||||
// As all our JNI methods return -errno on error we need to compare with the negative errno codes.
|
||||
public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
|
||||
public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
|
||||
public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
|
||||
public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
|
||||
public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
|
||||
public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
|
||||
public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
|
||||
|
||||
/**
|
||||
* Holds the mappings for errno codes to String messages.
|
||||
* This eliminates the need to call back into JNI to get the right String message on an exception
|
||||
* and thus is faster.
|
||||
*
|
||||
* The array length of 512 should be more then enough because errno.h only holds < 200 codes.
|
||||
*/
|
||||
private static final String[] ERRORS = new String[512];
|
||||
|
||||
// Pre-instantiated exceptions which does not need any stacktrace and
|
||||
// can be thrown multiple times for performance reasons.
|
||||
static final ClosedChannelException CLOSED_CHANNEL_EXCEPTION;
|
||||
static final NativeIoException CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION;
|
||||
static final NativeIoException CONNECTION_RESET_EXCEPTION_WRITE;
|
||||
static final NativeIoException CONNECTION_RESET_EXCEPTION_WRITEV;
|
||||
static final NativeIoException CONNECTION_RESET_EXCEPTION_READ;
|
||||
static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDTO;
|
||||
static final NativeIoException CONNECTION_RESET_EXCEPTION_SENDMSG;
|
||||
|
||||
/**
|
||||
* <strong>Internal usage only!</strong>
|
||||
*/
|
||||
public static final class NativeIoException extends IOException {
|
||||
private static final long serialVersionUID = 8222160204268655526L;
|
||||
private final int expectedErr;
|
||||
public NativeIoException(String method, int expectedErr) {
|
||||
super(method);
|
||||
this.expectedErr = expectedErr;
|
||||
}
|
||||
|
||||
public int expectedErr() {
|
||||
return expectedErr;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
for (int i = 0; i < ERRORS.length; i++) {
|
||||
// This is ok as strerror returns 'Unknown error i' when the message is not known.
|
||||
ERRORS[i] = strError(i);
|
||||
}
|
||||
|
||||
CONNECTION_RESET_EXCEPTION_READ = newConnectionResetException("syscall:read(...)",
|
||||
ERRNO_ECONNRESET_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_WRITE = newConnectionResetException("syscall:write(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_WRITEV = newConnectionResetException("syscall:writev(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SENDTO = newConnectionResetException("syscall:sendto(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_RESET_EXCEPTION_SENDMSG = newConnectionResetException("syscall:sendmsg(...)",
|
||||
ERRNO_EPIPE_NEGATIVE);
|
||||
CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION = newConnectionResetException("syscall:shutdown(...)",
|
||||
ERRNO_ENOTCONN_NEGATIVE);
|
||||
CLOSED_CHANNEL_EXCEPTION = new ClosedChannelException();
|
||||
CLOSED_CHANNEL_EXCEPTION.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
||||
}
|
||||
|
||||
static ConnectException newConnectException(String method, int err) {
|
||||
return new ConnectException(method + "() failed: " + ERRORS[-err]);
|
||||
}
|
||||
|
||||
public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
|
||||
NativeIoException exception = newIOException(method, errnoNegative);
|
||||
exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
|
||||
return exception;
|
||||
}
|
||||
|
||||
public static NativeIoException newIOException(String method, int err) {
|
||||
return new NativeIoException(method + "() failed: " + ERRORS[-err], err);
|
||||
}
|
||||
|
||||
public static int ioResult(String method, int err, NativeIoException resetCause) throws IOException {
|
||||
// network stack saturated... try again later
|
||||
if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
|
||||
return 0;
|
||||
}
|
||||
if (err == resetCause.expectedErr()) {
|
||||
throw resetCause;
|
||||
}
|
||||
if (err == ERRNO_EBADF_NEGATIVE || err == ERRNO_ENOTCONN_NEGATIVE) {
|
||||
throw CLOSED_CHANNEL_EXCEPTION;
|
||||
}
|
||||
// TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
|
||||
// all other errors it may be better to just include a stack trace.
|
||||
throw newIOException(method, err);
|
||||
}
|
||||
|
||||
private static native int errnoEBADF();
|
||||
private static native int errnoEPIPE();
|
||||
private static native int errnoECONNRESET();
|
||||
private static native int errnoENOTCONN();
|
||||
private static native int errnoEAGAIN();
|
||||
private static native int errnoEWOULDBLOCK();
|
||||
private static native int errnoEINPROGRESS();
|
||||
private static native String strError(int err);
|
||||
|
||||
private Errors() { }
|
||||
}
|
@ -15,11 +15,15 @@
|
||||
*/
|
||||
package io.netty.channel.unix;
|
||||
|
||||
import io.netty.channel.epoll.Native;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_READ;
|
||||
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_WRITE;
|
||||
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_WRITEV;
|
||||
import static io.netty.channel.unix.Errors.ioResult;
|
||||
import static io.netty.channel.unix.Errors.newIOException;
|
||||
import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
|
||||
/**
|
||||
@ -27,7 +31,6 @@ import static io.netty.util.internal.ObjectUtil.checkNotNull;
|
||||
* {@link FileDescriptor} for it.
|
||||
*/
|
||||
public class FileDescriptor {
|
||||
|
||||
private final int fd;
|
||||
private volatile boolean open = true;
|
||||
|
||||
@ -50,7 +53,10 @@ public class FileDescriptor {
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
open = false;
|
||||
close(fd);
|
||||
int res = close(fd);
|
||||
if (res < 0) {
|
||||
throw newIOException("close", res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -60,6 +66,60 @@ public class FileDescriptor {
|
||||
return open;
|
||||
}
|
||||
|
||||
public final int write(ByteBuffer buf, int pos, int limit) throws IOException {
|
||||
int res = write(fd, buf, pos, limit);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("write", res, CONNECTION_RESET_EXCEPTION_WRITE);
|
||||
}
|
||||
|
||||
public final int writeAddress(long address, int pos, int limit) throws IOException {
|
||||
int res = writeAddress(fd, address, pos, limit);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("writeAddress", res, CONNECTION_RESET_EXCEPTION_WRITE);
|
||||
}
|
||||
|
||||
public final long writev(ByteBuffer[] buffers, int offset, int length) throws IOException {
|
||||
long res = writev(fd, buffers, offset, length);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("writev", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
|
||||
}
|
||||
|
||||
public final long writevAddresses(long memoryAddress, int length) throws IOException {
|
||||
long res = writevAddresses(fd, memoryAddress, length);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("writevAddresses", (int) res, CONNECTION_RESET_EXCEPTION_WRITEV);
|
||||
}
|
||||
|
||||
public final int read(ByteBuffer buf, int pos, int limit) throws IOException {
|
||||
int res = read(fd, buf, pos, limit);
|
||||
if (res > 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) {
|
||||
return -1;
|
||||
}
|
||||
return ioResult("read", res, CONNECTION_RESET_EXCEPTION_READ);
|
||||
}
|
||||
|
||||
public final int readAddress(long address, int pos, int limit) throws IOException {
|
||||
int res = readAddress(fd, address, pos, limit);
|
||||
if (res > 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == 0) {
|
||||
return -1;
|
||||
}
|
||||
return ioResult("readAddress", res, CONNECTION_RESET_EXCEPTION_READ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FileDescriptor{" +
|
||||
@ -84,8 +144,6 @@ public class FileDescriptor {
|
||||
return fd;
|
||||
}
|
||||
|
||||
private static native int close(int fd);
|
||||
|
||||
/**
|
||||
* Open a new {@link FileDescriptor} for the given path.
|
||||
*/
|
||||
@ -93,7 +151,7 @@ public class FileDescriptor {
|
||||
checkNotNull(path, "path");
|
||||
int res = open(path);
|
||||
if (res < 0) {
|
||||
throw Native.newIOException("open", res);
|
||||
throw newIOException("open", res);
|
||||
}
|
||||
return new FileDescriptor(res);
|
||||
}
|
||||
@ -105,5 +163,27 @@ public class FileDescriptor {
|
||||
return from(checkNotNull(file, "file").getPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [0] = read end, [1] = write end
|
||||
*/
|
||||
public static FileDescriptor[] pipe() throws IOException {
|
||||
long res = newPipe();
|
||||
if (res < 0) {
|
||||
throw newIOException("newPipe", (int) res);
|
||||
}
|
||||
return new FileDescriptor[]{new FileDescriptor((int) (res >>> 32)), new FileDescriptor((int) res)};
|
||||
}
|
||||
|
||||
private static native int open(String path);
|
||||
private static native int close(int fd);
|
||||
|
||||
private static native int write(int fd, ByteBuffer buf, int pos, int limit);
|
||||
private static native int writeAddress(int fd, long address, int pos, int limit);
|
||||
private static native long writev(int fd, ByteBuffer[] buffers, int offset, int length);
|
||||
private static native long writevAddresses(int fd, long memoryAddress, int length);
|
||||
|
||||
private static native int read(int fd, ByteBuffer buf, int pos, int limit);
|
||||
private static native int readAddress(int fd, long address, int pos, int limit);
|
||||
|
||||
private static native long newPipe();
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
package io.netty.channel.unix;
|
||||
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* <strong>Internal usage only!</strong>
|
||||
*/
|
||||
public final class NativeInetAddress {
|
||||
private static final byte[] IPV4_MAPPED_IPV6_PREFIX = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, (byte) 0xff, (byte) 0xff };
|
||||
final byte[] address;
|
||||
final int scopeId;
|
||||
|
||||
public static NativeInetAddress newInstance(InetAddress addr) {
|
||||
byte[] bytes = addr.getAddress();
|
||||
if (addr instanceof Inet6Address) {
|
||||
return new NativeInetAddress(bytes, ((Inet6Address) addr).getScopeId());
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
return new NativeInetAddress(ipv4MappedIpv6Address(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
public NativeInetAddress(byte[] address, int scopeId) {
|
||||
this.address = address;
|
||||
this.scopeId = scopeId;
|
||||
}
|
||||
|
||||
public NativeInetAddress(byte[] address) {
|
||||
this(address, 0);
|
||||
}
|
||||
|
||||
public byte[] address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public int scopeId() {
|
||||
return scopeId;
|
||||
}
|
||||
|
||||
public static byte[] ipv4MappedIpv6Address(byte[] ipv4) {
|
||||
byte[] address = new byte[16];
|
||||
System.arraycopy(IPV4_MAPPED_IPV6_PREFIX, 0, address, 0, IPV4_MAPPED_IPV6_PREFIX.length);
|
||||
System.arraycopy(ipv4, 0, address, 12, ipv4.length);
|
||||
return address;
|
||||
}
|
||||
|
||||
public static InetSocketAddress address(byte[] addr, int offset, int len) {
|
||||
// The last 4 bytes are always the port
|
||||
final int port = decodeInt(addr, offset + len - 4);
|
||||
final InetAddress address;
|
||||
|
||||
try {
|
||||
switch (len) {
|
||||
// 8 bytes:
|
||||
// - 4 == ipaddress
|
||||
// - 4 == port
|
||||
case 8:
|
||||
byte[] ipv4 = new byte[4];
|
||||
System.arraycopy(addr, offset, ipv4, 0, 4);
|
||||
address = InetAddress.getByAddress(ipv4);
|
||||
break;
|
||||
|
||||
// 24 bytes:
|
||||
// - 16 == ipaddress
|
||||
// - 4 == scopeId
|
||||
// - 4 == port
|
||||
case 24:
|
||||
byte[] ipv6 = new byte[16];
|
||||
System.arraycopy(addr, offset, ipv6, 0, 16);
|
||||
int scopeId = decodeInt(addr, offset + len - 8);
|
||||
address = Inet6Address.getByAddress(null, ipv6, scopeId);
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
return new InetSocketAddress(address, port);
|
||||
} catch (UnknownHostException e) {
|
||||
throw new Error("Should never happen", e);
|
||||
}
|
||||
}
|
||||
|
||||
static int decodeInt(byte[] addr, int index) {
|
||||
return (addr[index] & 0xff) << 24 |
|
||||
(addr[index + 1] & 0xff) << 16 |
|
||||
(addr[index + 2] & 0xff) << 8 |
|
||||
addr[index + 3] & 0xff;
|
||||
}
|
||||
}
|
@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright 2015 The Netty Project
|
||||
*
|
||||
* The Netty Project licenses this file to you 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.
|
||||
*/
|
||||
package io.netty.channel.unix;
|
||||
|
||||
import io.netty.channel.ChannelException;
|
||||
import io.netty.util.CharsetUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static io.netty.channel.unix.Errors.CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION;
|
||||
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_SENDMSG;
|
||||
import static io.netty.channel.unix.Errors.CONNECTION_RESET_EXCEPTION_SENDTO;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EAGAIN_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EINPROGRESS_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ERRNO_EWOULDBLOCK_NEGATIVE;
|
||||
import static io.netty.channel.unix.Errors.ioResult;
|
||||
import static io.netty.channel.unix.Errors.newConnectException;
|
||||
import static io.netty.channel.unix.Errors.newIOException;
|
||||
import static io.netty.channel.unix.NativeInetAddress.address;
|
||||
import static io.netty.channel.unix.NativeInetAddress.ipv4MappedIpv6Address;
|
||||
|
||||
/**
|
||||
* Provides a JNI bridge to native socket operations.
|
||||
* <strong>Internal usage only!</strong>
|
||||
*/
|
||||
public final class Socket extends FileDescriptor {
|
||||
private volatile boolean inputShutdown;
|
||||
private volatile boolean outputShutdown;
|
||||
|
||||
public Socket(int fd) {
|
||||
super(fd);
|
||||
}
|
||||
|
||||
public void shutdown(boolean read, boolean write) throws IOException {
|
||||
inputShutdown = read || inputShutdown;
|
||||
outputShutdown = write || outputShutdown;
|
||||
int res = shutdown(intValue(), read, write);
|
||||
if (res < 0) {
|
||||
ioResult("shutdown", res, CONNECTION_NOT_CONNECTED_SHUTDOWN_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isShutdown() {
|
||||
return isInputShutdown() && isOutputShutdown();
|
||||
}
|
||||
|
||||
public boolean isInputShutdown() {
|
||||
return inputShutdown;
|
||||
}
|
||||
|
||||
public boolean isOutputShutdown() {
|
||||
return outputShutdown;
|
||||
}
|
||||
|
||||
public int sendTo(ByteBuffer buf, int pos, int limit, InetAddress addr, int port) throws IOException {
|
||||
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
|
||||
// to be called frequently
|
||||
byte[] address;
|
||||
int scopeId;
|
||||
if (addr instanceof Inet6Address) {
|
||||
address = addr.getAddress();
|
||||
scopeId = ((Inet6Address) addr).getScopeId();
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
scopeId = 0;
|
||||
address = ipv4MappedIpv6Address(addr.getAddress());
|
||||
}
|
||||
int res = sendTo(intValue(), buf, pos, limit, address, scopeId, port);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendTo", res, CONNECTION_RESET_EXCEPTION_SENDTO);
|
||||
}
|
||||
|
||||
public int sendToAddress(long memoryAddress, int pos, int limit, InetAddress addr, int port)
|
||||
throws IOException {
|
||||
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
|
||||
// to be called frequently
|
||||
byte[] address;
|
||||
int scopeId;
|
||||
if (addr instanceof Inet6Address) {
|
||||
address = addr.getAddress();
|
||||
scopeId = ((Inet6Address) addr).getScopeId();
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
scopeId = 0;
|
||||
address = ipv4MappedIpv6Address(addr.getAddress());
|
||||
}
|
||||
int res = sendToAddress(intValue(), memoryAddress, pos, limit, address, scopeId, port);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendToAddress", res, CONNECTION_RESET_EXCEPTION_SENDTO);
|
||||
}
|
||||
|
||||
public int sendToAddresses(long memoryAddress, int length, InetAddress addr, int port) throws IOException {
|
||||
// just duplicate the toNativeInetAddress code here to minimize object creation as this method is expected
|
||||
// to be called frequently
|
||||
byte[] address;
|
||||
int scopeId;
|
||||
if (addr instanceof Inet6Address) {
|
||||
address = addr.getAddress();
|
||||
scopeId = ((Inet6Address) addr).getScopeId();
|
||||
} else {
|
||||
// convert to ipv4 mapped ipv6 address;
|
||||
scopeId = 0;
|
||||
address = ipv4MappedIpv6Address(addr.getAddress());
|
||||
}
|
||||
int res = sendToAddresses(intValue(), memoryAddress, length, address, scopeId, port);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
return ioResult("sendToAddresses", res, CONNECTION_RESET_EXCEPTION_SENDMSG);
|
||||
}
|
||||
|
||||
public DatagramSocketAddress recvFrom(ByteBuffer buf, int pos, int limit) throws IOException {
|
||||
return recvFrom(intValue(), buf, pos, limit);
|
||||
}
|
||||
|
||||
public DatagramSocketAddress recvFromAddress(long memoryAddress, int pos, int limit) throws IOException {
|
||||
return recvFromAddress(intValue(), memoryAddress, pos, limit);
|
||||
}
|
||||
|
||||
public boolean connect(SocketAddress socketAddress) throws IOException {
|
||||
int res;
|
||||
if (socketAddress instanceof InetSocketAddress) {
|
||||
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
|
||||
NativeInetAddress address = NativeInetAddress.newInstance(inetSocketAddress.getAddress());
|
||||
res = connect(intValue(), address.address, address.scopeId, inetSocketAddress.getPort());
|
||||
} else if (socketAddress instanceof DomainSocketAddress) {
|
||||
DomainSocketAddress unixDomainSocketAddress = (DomainSocketAddress) socketAddress;
|
||||
res = connectDomainSocket(intValue(), unixDomainSocketAddress.path().getBytes(CharsetUtil.UTF_8));
|
||||
} else {
|
||||
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
|
||||
}
|
||||
if (res < 0) {
|
||||
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
|
||||
// connect not complete yet need to wait for EPOLLOUT event
|
||||
return false;
|
||||
}
|
||||
throw newConnectException("connect", res);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean finishConnect() throws IOException {
|
||||
int res = finishConnect(intValue());
|
||||
if (res < 0) {
|
||||
if (res == ERRNO_EINPROGRESS_NEGATIVE) {
|
||||
// connect still in progress
|
||||
return false;
|
||||
}
|
||||
throw newConnectException("finishConnect", res);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void bind(SocketAddress socketAddress) throws IOException {
|
||||
if (socketAddress instanceof InetSocketAddress) {
|
||||
InetSocketAddress addr = (InetSocketAddress) socketAddress;
|
||||
NativeInetAddress address = NativeInetAddress.newInstance(addr.getAddress());
|
||||
int res = bind(intValue(), address.address, address.scopeId, addr.getPort());
|
||||
if (res < 0) {
|
||||
throw newIOException("bind", res);
|
||||
}
|
||||
} else if (socketAddress instanceof DomainSocketAddress) {
|
||||
DomainSocketAddress addr = (DomainSocketAddress) socketAddress;
|
||||
int res = bindDomainSocket(intValue(), addr.path().getBytes(CharsetUtil.UTF_8));
|
||||
if (res < 0) {
|
||||
throw newIOException("bind", res);
|
||||
}
|
||||
} else {
|
||||
throw new Error("Unexpected SocketAddress implementation " + socketAddress);
|
||||
}
|
||||
}
|
||||
|
||||
public void listen(int backlog) throws IOException {
|
||||
int res = listen(intValue(), backlog);
|
||||
if (res < 0) {
|
||||
throw newIOException("listen", res);
|
||||
}
|
||||
}
|
||||
|
||||
public int accept(byte[] addr) throws IOException {
|
||||
int res = accept(intValue(), addr);
|
||||
if (res >= 0) {
|
||||
return res;
|
||||
}
|
||||
if (res == ERRNO_EAGAIN_NEGATIVE || res == ERRNO_EWOULDBLOCK_NEGATIVE) {
|
||||
// Everything consumed so just return -1 here.
|
||||
return -1;
|
||||
}
|
||||
throw newIOException("accept", res);
|
||||
}
|
||||
|
||||
public InetSocketAddress remoteAddress() {
|
||||
byte[] addr = remoteAddress(intValue());
|
||||
// addr may be null if getpeername failed.
|
||||
// See https://github.com/netty/netty/issues/3328
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
return address(addr, 0, addr.length);
|
||||
}
|
||||
|
||||
public InetSocketAddress localAddress() {
|
||||
byte[] addr = localAddress(intValue());
|
||||
// addr may be null if getpeername failed.
|
||||
// See https://github.com/netty/netty/issues/3328
|
||||
if (addr == null) {
|
||||
return null;
|
||||
}
|
||||
return address(addr, 0, addr.length);
|
||||
}
|
||||
|
||||
public int getReceiveBufferSize() {
|
||||
return getReceiveBufferSize(intValue());
|
||||
}
|
||||
|
||||
public int getSendBufferSize() {
|
||||
return getSendBufferSize(intValue());
|
||||
}
|
||||
|
||||
public boolean isKeepAlive() {
|
||||
return isKeepAlive(intValue()) != 0;
|
||||
}
|
||||
|
||||
public boolean isTcpNoDelay() {
|
||||
return isTcpNoDelay(intValue()) != 0;
|
||||
}
|
||||
|
||||
public boolean isTcpCork() {
|
||||
return isTcpCork(intValue()) != 0;
|
||||
}
|
||||
|
||||
public int getSoLinger() {
|
||||
return getSoLinger(intValue());
|
||||
}
|
||||
|
||||
public int getSoError() {
|
||||
return getSoError(intValue());
|
||||
}
|
||||
|
||||
public void setKeepAlive(boolean keepAlive) {
|
||||
setKeepAlive(intValue(), keepAlive ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setReceiveBufferSize(int receiveBufferSize) {
|
||||
setReceiveBufferSize(intValue(), receiveBufferSize);
|
||||
}
|
||||
|
||||
public void setSendBufferSize(int sendBufferSize) {
|
||||
setSendBufferSize(intValue(), sendBufferSize);
|
||||
}
|
||||
|
||||
public void setTcpNoDelay(boolean tcpNoDelay) {
|
||||
setTcpNoDelay(intValue(), tcpNoDelay ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setTcpCork(boolean tcpCork) {
|
||||
setTcpCork(intValue(), tcpCork ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setSoLinger(int soLinger) {
|
||||
setSoLinger(intValue(), soLinger);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Socket{" +
|
||||
"fd=" + intValue() +
|
||||
'}';
|
||||
}
|
||||
|
||||
public static Socket newSocketStream() {
|
||||
int res = newSocketStreamFd();
|
||||
if (res < 0) {
|
||||
throw new ChannelException(newIOException("newSocketStream", res));
|
||||
}
|
||||
return new Socket(res);
|
||||
}
|
||||
|
||||
public static Socket newSocketDgram() {
|
||||
int res = newSocketDgramFd();
|
||||
if (res < 0) {
|
||||
throw new ChannelException(newIOException("newSocketDgram", res));
|
||||
}
|
||||
return new Socket(res);
|
||||
}
|
||||
|
||||
public static Socket newSocketDomain() {
|
||||
int res = newSocketDomainFd();
|
||||
if (res < 0) {
|
||||
throw new ChannelException(newIOException("newSocketDomain", res));
|
||||
}
|
||||
return new Socket(res);
|
||||
}
|
||||
|
||||
private static native int shutdown(int fd, boolean read, boolean write);
|
||||
private static native int connect(int fd, byte[] address, int scopeId, int port);
|
||||
private static native int connectDomainSocket(int fd, byte[] path);
|
||||
private static native int finishConnect(int fd);
|
||||
private static native int bind(int fd, byte[] address, int scopeId, int port);
|
||||
private static native int bindDomainSocket(int fd, byte[] path);
|
||||
private static native int listen(int fd, int backlog);
|
||||
private static native int accept(int fd, byte[] addr);
|
||||
|
||||
private static native byte[] remoteAddress(int fd);
|
||||
private static native byte[] localAddress(int fd);
|
||||
|
||||
private static native int sendTo(
|
||||
int fd, ByteBuffer buf, int pos, int limit, byte[] address, int scopeId, int port);
|
||||
private static native int sendToAddress(
|
||||
int fd, long memoryAddress, int pos, int limit, byte[] address, int scopeId, int port);
|
||||
private static native int sendToAddresses(
|
||||
int fd, long memoryAddress, int length, byte[] address, int scopeId, int port);
|
||||
|
||||
private static native DatagramSocketAddress recvFrom(
|
||||
int fd, ByteBuffer buf, int pos, int limit) throws IOException;
|
||||
private static native DatagramSocketAddress recvFromAddress(
|
||||
int fd, long memoryAddress, int pos, int limit) throws IOException;
|
||||
|
||||
private static native int newSocketStreamFd();
|
||||
private static native int newSocketDgramFd();
|
||||
private static native int newSocketDomainFd();
|
||||
|
||||
private static native int getReceiveBufferSize(int fd);
|
||||
private static native int getSendBufferSize(int fd);
|
||||
private static native int isKeepAlive(int fd);
|
||||
private static native int isTcpNoDelay(int fd);
|
||||
private static native int isTcpCork(int fd);
|
||||
private static native int getSoLinger(int fd);
|
||||
private static native int getSoError(int fd);
|
||||
|
||||
private static native void setKeepAlive(int fd, int keepAlive);
|
||||
private static native void setReceiveBufferSize(int fd, int receiveBufferSize);
|
||||
private static native void setSendBufferSize(int fd, int sendBufferSize);
|
||||
private static native void setTcpNoDelay(int fd, int tcpNoDelay);
|
||||
private static native void setTcpCork(int fd, int tcpCork);
|
||||
private static native void setSoLinger(int fd, int soLinger);
|
||||
}
|
@ -145,7 +145,6 @@ public class EpollSocketChannelTest {
|
||||
Bootstrap b = new Bootstrap();
|
||||
b.group(group);
|
||||
b.channel(channelClass);
|
||||
b.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
b.remoteAddress(serverChannel.localAddress());
|
||||
b.handler(new MyInitializer());
|
||||
clientChannel = b.connect().syncUninterruptibly().channel();
|
||||
|
@ -23,6 +23,8 @@ import java.net.Inet6Address;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static io.netty.channel.unix.NativeInetAddress.address;
|
||||
|
||||
public class NativeTest {
|
||||
|
||||
@Test
|
||||
@ -32,7 +34,7 @@ public class NativeTest {
|
||||
ByteBuffer buffer = ByteBuffer.wrap(bytes);
|
||||
buffer.put(inetAddress.getAddress().getAddress());
|
||||
buffer.putInt(inetAddress.getPort());
|
||||
Assert.assertEquals(inetAddress, Native.address(buffer.array(), 0, bytes.length));
|
||||
Assert.assertEquals(inetAddress, address(buffer.array(), 0, bytes.length));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -44,6 +46,6 @@ public class NativeTest {
|
||||
buffer.put(address.getAddress());
|
||||
buffer.putInt(address.getScopeId());
|
||||
buffer.putInt(inetAddress.getPort());
|
||||
Assert.assertEquals(inetAddress, Native.address(buffer.array(), 0, bytes.length));
|
||||
Assert.assertEquals(inetAddress, address(buffer.array(), 0, bytes.length));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user