Add support for TCP_DEFER_ACCEPT and TCP_QUICKACK

Motivation:

When using the native transport have support for TCP_DEFER_ACCEPT or / and TCP_QUICKACK can be useful.

Modifications:

- Add support for TCP_DEFER_ACCEPT and TCP_QUICKACK
- Ad unit tests

Result:

TCP_DEFER_ACCEPT and TCP_QUICKACK are supported now.

Conflicts:
	transport-native-epoll/src/main/java/io/netty/channel/epoll/EpollChannelOption.java
This commit is contained in:
Norman Maurer 2016-03-04 10:32:11 +01:00
parent a47c03c3d7
commit 5317c7b648
7 changed files with 180 additions and 5 deletions

View File

@ -582,6 +582,14 @@ static void netty_unix_socket_setSoLinger(JNIEnv* env, jclass clazz, jint fd, ji
netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger)); netty_unix_socket_setOption(env, fd, SOL_SOCKET, SO_LINGER, &solinger, sizeof(solinger));
} }
static void netty_unix_socket_setTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval));
}
static void netty_unix_socket_setTcpQuickAck(JNIEnv* env, jclass clazz, jint fd, jint optval) {
netty_unix_socket_setOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval));
}
static jint netty_unix_socket_isKeepAlive(JNIEnv* env, jclass clazz, jint fd) { static jint netty_unix_socket_isKeepAlive(JNIEnv* env, jclass clazz, jint fd) {
int optval; int optval;
if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) { if (netty_unix_socket_getOption(env, fd, SOL_SOCKET, SO_KEEPALIVE, &optval, sizeof(optval)) == -1) {
@ -641,6 +649,22 @@ static jint netty_unix_socket_getSoError(JNIEnv* env, jclass clazz, jint fd) {
} }
return optval; return optval;
} }
static jint netty_unix_socket_getTcpDeferAccept(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
static jint netty_unix_socket_isTcpQuickAck(JNIEnv* env, jclass clazz, jint fd) {
int optval;
if (netty_unix_socket_getOption(env, fd, IPPROTO_TCP, TCP_QUICKACK, &optval, sizeof(optval)) == -1) {
return -1;
}
return optval;
}
// JNI Registered Methods End // JNI Registered Methods End
// JNI Method Registration Table Begin // JNI Method Registration Table Begin
@ -669,13 +693,17 @@ static const JNINativeMethod fixed_method_table[] = {
{ "setKeepAlive", "(II)V", (void *) netty_unix_socket_setKeepAlive }, { "setKeepAlive", "(II)V", (void *) netty_unix_socket_setKeepAlive },
{ "setTcpCork", "(II)V", (void *) netty_unix_socket_setTcpCork }, { "setTcpCork", "(II)V", (void *) netty_unix_socket_setTcpCork },
{ "setSoLinger", "(II)V", (void *) netty_unix_socket_setSoLinger }, { "setSoLinger", "(II)V", (void *) netty_unix_socket_setSoLinger },
{ "setTcpDeferAccept", "(II)V", (void *) netty_unix_socket_setTcpDeferAccept },
{ "setTcpQuickAck", "(II)V", (void *) netty_unix_socket_setTcpQuickAck },
{ "isKeepAlive", "(I)I", (void *) netty_unix_socket_isKeepAlive }, { "isKeepAlive", "(I)I", (void *) netty_unix_socket_isKeepAlive },
{ "isTcpNoDelay", "(I)I", (void *) netty_unix_socket_isTcpNoDelay }, { "isTcpNoDelay", "(I)I", (void *) netty_unix_socket_isTcpNoDelay },
{ "getReceiveBufferSize", "(I)I", (void *) netty_unix_socket_getReceiveBufferSize }, { "getReceiveBufferSize", "(I)I", (void *) netty_unix_socket_getReceiveBufferSize },
{ "getSendBufferSize", "(I)I", (void *) netty_unix_socket_getSendBufferSize }, { "getSendBufferSize", "(I)I", (void *) netty_unix_socket_getSendBufferSize },
{ "isTcpCork", "(I)I", (void *) netty_unix_socket_isTcpCork }, { "isTcpCork", "(I)I", (void *) netty_unix_socket_isTcpCork },
{ "getSoLinger", "(I)I", (void *) netty_unix_socket_getSoLinger }, { "getSoLinger", "(I)I", (void *) netty_unix_socket_getSoLinger },
{ "getSoError", "(I)I", (void *) netty_unix_socket_getSoError } { "getSoError", "(I)I", (void *) netty_unix_socket_getSoError },
{ "getTcpDeferAccept", "(I)I", (void *) netty_unix_socket_getTcpDeferAccept },
{ "isTcpQuickAck", "(I)I", (void *) netty_unix_socket_isTcpQuickAck }
}; };
static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]); static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]);

View File

@ -32,6 +32,8 @@ public final class EpollChannelOption<T> extends ChannelOption<T> {
public static final ChannelOption<Integer> TCP_USER_TIMEOUT = valueOf("TCP_USER_TIMEOUT"); public static final ChannelOption<Integer> TCP_USER_TIMEOUT = valueOf("TCP_USER_TIMEOUT");
public static final ChannelOption<Boolean> IP_FREEBIND = valueOf("IP_FREEBIND"); public static final ChannelOption<Boolean> IP_FREEBIND = valueOf("IP_FREEBIND");
public static final ChannelOption<Integer> TCP_FASTOPEN = valueOf("TCP_FASTOPEN"); public static final ChannelOption<Integer> TCP_FASTOPEN = valueOf("TCP_FASTOPEN");
public static final ChannelOption<Integer> TCP_DEFER_ACCEPT = valueOf("TCP_DEFER_ACCEPT");
public static final ChannelOption<Integer> TCP_QUICKACK = valueOf("TCP_QUICKACK");
public static final ChannelOption<DomainSocketReadMode> DOMAIN_SOCKET_READ_MODE = public static final ChannelOption<DomainSocketReadMode> DOMAIN_SOCKET_READ_MODE =
valueOf("DOMAIN_SOCKET_READ_MODE"); valueOf("DOMAIN_SOCKET_READ_MODE");

View File

@ -38,7 +38,8 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
@Override @Override
public Map<ChannelOption<?>, Object> getOptions() { public Map<ChannelOption<?>, Object> getOptions() {
return getOptions(super.getOptions(), EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_FREEBIND); return getOptions(super.getOptions(), EpollChannelOption.SO_REUSEPORT, EpollChannelOption.IP_FREEBIND,
EpollChannelOption.TCP_DEFER_ACCEPT);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -50,6 +51,9 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
if (option == EpollChannelOption.IP_FREEBIND) { if (option == EpollChannelOption.IP_FREEBIND) {
return (T) Boolean.valueOf(isFreeBind()); return (T) Boolean.valueOf(isFreeBind());
} }
if (option == EpollChannelOption.TCP_DEFER_ACCEPT) {
return (T) Integer.valueOf(getTcpDeferAccept());
}
return super.getOption(option); return super.getOption(option);
} }
@ -65,6 +69,8 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value; final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value;
setTcpMd5Sig(m); setTcpMd5Sig(m);
} else if (option == EpollChannelOption.TCP_DEFER_ACCEPT) {
setTcpDeferAccept((Integer) value);
} else { } else {
return super.setOption(option, value); return super.setOption(option, value);
} }
@ -194,4 +200,19 @@ public final class EpollServerSocketChannelConfig extends EpollServerChannelConf
Native.setIpFreeBind(channel.fd().intValue(), freeBind ? 1: 0); Native.setIpFreeBind(channel.fd().intValue(), freeBind ? 1: 0);
return this; return this;
} }
/**
* Set the {@code TCP_DEFER_ACCEPT} option on the socket. See {@code man 7 tcp} for more details.
*/
public EpollServerSocketChannelConfig setTcpDeferAccept(int deferAccept) {
channel.fd().setTcpDeferAccept(deferAccept);
return this;
}
/**
* Returns a positive value if <a href="http://linux.die.net/man/7/tcp">TCP_DEFER_ACCEPT</a> is enabled.
*/
public int getTcpDeferAccept() {
return channel.fd().getTcpDeferAccept();
}
} }

View File

@ -51,7 +51,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS, SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS,
ALLOW_HALF_CLOSURE, EpollChannelOption.TCP_CORK, EpollChannelOption.TCP_NOTSENT_LOWAT, ALLOW_HALF_CLOSURE, EpollChannelOption.TCP_CORK, EpollChannelOption.TCP_NOTSENT_LOWAT,
EpollChannelOption.TCP_KEEPCNT, EpollChannelOption.TCP_KEEPIDLE, EpollChannelOption.TCP_KEEPINTVL, EpollChannelOption.TCP_KEEPCNT, EpollChannelOption.TCP_KEEPIDLE, EpollChannelOption.TCP_KEEPINTVL,
EpollChannelOption.TCP_MD5SIG); EpollChannelOption.TCP_MD5SIG, EpollChannelOption.TCP_QUICKACK);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -99,6 +99,9 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
if (option == EpollChannelOption.TCP_USER_TIMEOUT) { if (option == EpollChannelOption.TCP_USER_TIMEOUT) {
return (T) Integer.valueOf(getTcpUserTimeout()); return (T) Integer.valueOf(getTcpUserTimeout());
} }
if (option == EpollChannelOption.TCP_QUICKACK) {
return (T) Boolean.valueOf(isTcpQuickAck());
}
return super.getOption(option); return super.getOption(option);
} }
@ -138,6 +141,8 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value; final Map<InetAddress, byte[]> m = (Map<InetAddress, byte[]>) value;
setTcpMd5Sig(m); setTcpMd5Sig(m);
} else if (option == EpollChannelOption.TCP_QUICKACK) {
setTcpQuickAck((Boolean) value);
} else { } else {
return super.setOption(option, value); return super.setOption(option, value);
} }
@ -323,7 +328,7 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
return this; return this;
} }
/* /**
* Set the {@code TCP_MD5SIG} option on the socket. See {@code linux/tcp.h} for more details. * Set the {@code TCP_MD5SIG} option on the socket. See {@code linux/tcp.h} for more details.
* Keys can only be set on, not read to prevent a potential leak, as they are confidential. * Keys can only be set on, not read to prevent a potential leak, as they are confidential.
* Allowing them being read would mean anyone with access to the channel could get them. * Allowing them being read would mean anyone with access to the channel could get them.
@ -333,6 +338,23 @@ public final class EpollSocketChannelConfig extends EpollChannelConfig implement
return this; return this;
} }
/**
* Set the {@code TCP_QUICKACK} option on the socket. See <a href="http://linux.die.net/man/7/tcp">TCP_QUICKACK</a>
* for more details.
*/
public EpollSocketChannelConfig setTcpQuickAck(boolean quickAck) {
channel.fd().setTcpQuickAck(quickAck);
return this;
}
/**
* Returns {@code true} if <a href="http://linux.die.net/man/7/tcp">TCP_QUICKACK</a> is enabled,
* {@code false} otherwise.
*/
public boolean isTcpQuickAck() {
return channel.fd().isTcpQuickAck();
}
@Override @Override
public boolean isAllowHalfClosure() { public boolean isAllowHalfClosure() {
return allowHalfClosure; return allowHalfClosure;

View File

@ -263,6 +263,14 @@ public final class Socket extends FileDescriptor {
return getSoLinger(intValue()); return getSoLinger(intValue());
} }
public int getTcpDeferAccept() {
return getTcpDeferAccept(intValue());
}
public boolean isTcpQuickAck() {
return isTcpQuickAck(intValue()) != 0;
}
public int getSoError() { public int getSoError() {
return getSoError(intValue()); return getSoError(intValue());
} }
@ -291,6 +299,14 @@ public final class Socket extends FileDescriptor {
setSoLinger(intValue(), soLinger); setSoLinger(intValue(), soLinger);
} }
public void setTcpDeferAccept(int deferAccept) {
setTcpDeferAccept(intValue(), deferAccept);
}
public void setTcpQuickAck(boolean quickAck) {
setTcpQuickAck(intValue(), quickAck ? 1 : 0);
}
@Override @Override
public String toString() { public String toString() {
return "Socket{" + return "Socket{" +
@ -357,6 +373,8 @@ public final class Socket extends FileDescriptor {
private static native int isTcpCork(int fd); private static native int isTcpCork(int fd);
private static native int getSoLinger(int fd); private static native int getSoLinger(int fd);
private static native int getSoError(int fd); private static native int getSoError(int fd);
private static native int getTcpDeferAccept(int fd);
private static native int isTcpQuickAck(int fd);
private static native void setKeepAlive(int fd, int keepAlive); private static native void setKeepAlive(int fd, int keepAlive);
private static native void setReceiveBufferSize(int fd, int receiveBufferSize); private static native void setReceiveBufferSize(int fd, int receiveBufferSize);
@ -364,4 +382,6 @@ public final class Socket extends FileDescriptor {
private static native void setTcpNoDelay(int fd, int tcpNoDelay); private static native void setTcpNoDelay(int fd, int tcpNoDelay);
private static native void setTcpCork(int fd, int tcpCork); private static native void setTcpCork(int fd, int tcpCork);
private static native void setSoLinger(int fd, int soLinger); private static native void setSoLinger(int fd, int soLinger);
private static native void setTcpDeferAccept(int fd, int deferAccept);
private static native void setTcpQuickAck(int fd, int quickAck);
} }

View File

@ -0,0 +1,70 @@
/*
* Copyright 2016 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.epoll;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.EventLoopGroup;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.net.InetSocketAddress;
import static org.junit.Assert.*;
public class EpollServerSocketChannelConfigTest {
private static EventLoopGroup group;
private static EpollServerSocketChannel ch;
@BeforeClass
public static void before() {
group = new EpollEventLoopGroup(1);
ServerBootstrap bootstrap = new ServerBootstrap();
ch = (EpollServerSocketChannel) bootstrap.group(group)
.channel(EpollServerSocketChannel.class)
.childHandler(new ChannelInboundHandlerAdapter())
.bind(new InetSocketAddress(0)).syncUninterruptibly().channel();
}
@AfterClass
public static void after() {
try {
ch.close().syncUninterruptibly();
} finally {
group.shutdownGracefully();
}
}
@Test
public void testTcpDeferAccept() {
ch.config().setTcpDeferAccept(0);
assertEquals(0, ch.config().getTcpDeferAccept());
ch.config().setTcpDeferAccept(10);
// The returned value may be bigger then what we set.
// See http://www.spinics.net/lists/netdev/msg117330.html
assertTrue(10 <= ch.config().getTcpDeferAccept());
}
@Test
public void testReusePort() {
ch.config().setReusePort(false);
assertFalse(ch.config().isReusePort());
ch.config().setReusePort(true);
assertTrue(ch.config().isReusePort());
}
}

View File

@ -47,8 +47,12 @@ public class EpollSocketChannelConfigTest {
@AfterClass @AfterClass
public static void after() { public static void after() {
try {
ch.close().syncUninterruptibly();
} finally {
group.shutdownGracefully(); group.shutdownGracefully();
} }
}
private long randLong(long min, long max) { private long randLong(long min, long max) {
return min + nextLong(max - min + 1); return min + nextLong(max - min + 1);
@ -110,4 +114,12 @@ public class EpollSocketChannelConfigTest {
ch.config().setTcpCork(true); ch.config().setTcpCork(true);
assertTrue(ch.config().isTcpCork()); assertTrue(ch.config().isTcpCork());
} }
@Test
public void testTcpQickAck() {
ch.config().setTcpQuickAck(false);
assertFalse(ch.config().isTcpQuickAck());
ch.config().setTcpQuickAck(true);
assertTrue(ch.config().isTcpQuickAck());
}
} }