Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Helper function to bind same random port multiple times in Netty #97

Merged
merged 2 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ protected void initChannel(DatagramChannel ch) {
private Bootstrap createClientBootstrap(InetSocketAddress destinationAddress) {
return new Bootstrap()
.group(eventLoopGroup)
.localAddress(0)
.remoteAddress(destinationAddress)
.channel(NioDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
import java.net.InetSocketAddress;
import java.time.Duration;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.AfterAll;
Expand All @@ -61,27 +60,14 @@
@EnabledOnOs(OS.LINUX)
public class MultithreadedMbedtlsNettyTest {
private final int threads = 4;
private final int serverPort = new Random().nextInt(32000) + 32000;
private final EventLoopGroup eventLoopGroup = new EpollEventLoopGroup(threads, new DefaultThreadFactory("pool", true));

private final SslConfig clientConf = SslConfig.client(new PskAuth("test", of("secret").getBytes()));
private final SslConfig serverConf = SslConfig.server(new PskAuth("test", of("secret").getBytes()));

private final Bootstrap clientBootstrap = new Bootstrap()
.group(eventLoopGroup)
.localAddress(0)
.channel(EpollDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) {
InetSocketAddress destinationAddress = localhost(serverPort);
ch.pipeline().addFirst("DTLS", new DtlsClientHandshakeChannelHandler(clientConf.newContext(destinationAddress), destinationAddress, SessionWriter.NO_OPS));
}
});

private final Bootstrap serverBootstrap = new Bootstrap()
.group(eventLoopGroup)
.localAddress(serverPort)
.localAddress(0)
.channel(EpollDatagramChannel.class)
.option(UnixChannelOption.SO_REUSEPORT, true)
.handler(new ChannelInitializer<DatagramChannel>() {
Expand Down Expand Up @@ -120,10 +106,22 @@ void multi_thread_server() throws Exception {
}

private String connectAndReadCurrentThreadName() throws IOException, CoapException {
InetSocketAddress serverAddress = localhost(((InetSocketAddress) serverBootstrap.config().localAddress()).getPort());
Bootstrap clientBootstrap = new Bootstrap()
.group(eventLoopGroup)
.remoteAddress(serverAddress)
.channel(EpollDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
protected void initChannel(DatagramChannel ch) {
ch.pipeline().addFirst("DTLS", new DtlsClientHandshakeChannelHandler(clientConf.newContext(serverAddress), serverAddress, SessionWriter.NO_OPS));
}
});

CoapClient client = CoapServer.builder()
.executor(eventLoopGroup)
.transport(new NettyCoapTransport(clientBootstrap, EMPTY_RESOLVER))
.buildClient(localhost(serverPort));
.buildClient(serverAddress);

String threadName = client.sendSync(get("/currentThread")).getPayloadString();
client.close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.unix.UnixChannelOption;
import java.net.InetSocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
Expand All @@ -51,7 +52,24 @@ public NettyCoapTransport(Bootstrap bootstrap, Function<DatagramPacket, Transpor

@Override
public void start() {
init(bootstrap.bind().syncUninterruptibly().channel());
if (bootstrap.config().remoteAddress() != null) {
init(bootstrap.connect().syncUninterruptibly().channel());
} else {
init(bootstrap.bind().syncUninterruptibly().channel());

// for random port update bootstrap with actual port but only if SO_REUSEPORT is enabled
if (bindToRandomPort() && reusePortIsEnabled()) {
bootstrap.localAddress(channel.localAddress());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it work so, that after the first server using this bootstrap is started, then all the following server using the same bootstrap will use the updated version of it (with the port properly set)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, exactly that way.

}
}
}

private Boolean reusePortIsEnabled() {
return (Boolean) bootstrap.config().options().getOrDefault(UnixChannelOption.SO_REUSEPORT, Boolean.FALSE);
}

private boolean bindToRandomPort() {
return ((InetSocketAddress) bootstrap.config().localAddress()).getPort() == 0;
}

void init(Channel channel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ private Bootstrap createEpollBootstrap() {
return new Bootstrap()
.group(eventLoopGroup)
.option(UnixChannelOption.SO_REUSEPORT, true)
.localAddress(65001) // bind multiple times on single port
.localAddress(0) // bind multiple times on single port
.channel(EpollDatagramChannel.class)
.handler(new ChannelInitializer<DatagramChannel>() {
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2022-2023 java-coap contributors (https://github.com/open-coap/java-coap)
* Copyright (C) 2022-2024 java-coap contributors (https://github.com/open-coap/java-coap)
* SPDX-License-Identifier: Apache-2.0
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -33,6 +33,7 @@
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -83,13 +84,14 @@ void should_exchange_messages() throws Exception {
@Test
void should_exchange_messages_with_multiple_clients() throws Exception {
// given
InetSocketAddress serverAddress = localhost(server.getLocalSocketAddress().getPort());
List<CoapClient> clients = new ArrayList(10);
for (int i = 0; i < 10; i++) {
clients.add(
CoapServer.builder()
.transport(new NettyCoapTransport(createBootstrap(0), EMPTY_RESOLVER))
.transport(new NettyCoapTransport(createBootstrap(0).remoteAddress(serverAddress), EMPTY_RESOLVER))
.executor(eventLoopGroup)
.buildClient(localhost(server.getLocalSocketAddress().getPort()))
.buildClient(serverAddress)
);
}

Expand Down