How to get callback on netty client after server establish sslConnection - java

My server expects clients to connect on two different sockets and order of connection is important. Client must connect on first channel ch1 and after SSL handshake server takes time to create user session. In Handler it looks like this:
#Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
log.debug("channelRegistered);
ctx.pipeline().get(SslHandler.class).handshakeFuture().addListener(
future -> initSession(ctx));
}
InitSession method create internal objects to track client. Only after initSession is complete server expect connection on second channel ch2 from this client.
I'm stuck with writing client code to perform this connection order.
Naive way is easy:
public static void main(String[] args) throws Exception {
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
SslContext sslContext = provideSslContext();
Bootstrap b = new Bootstrap();
b.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new Channelinitializer(sslContext));
Channel ch1 = b.connect("localhost", 8008).sync().channel();
Thread.sleep(1000);
Bootstrap b1 = new Bootstrap();
b1.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new Channelinitializer(sslContext));
Channel ch2 = b1.connect("localhost", 8009).sync().channel();
}finally {
workerGroup.shutdownGracefully();
}
}
After ch1 connect we just wait for some time to be sure that server perform all actions required.
How robust solution should look like? Is there any callback I can use to trigger ch2 connection? I'm using netty 4.0.36.Final

You can just retrieve the SslHandler from the pipeline and wait on the handshakeFuture or add a listener to it. Then when it is complete do the second connect.
Something like:
SslContext sslContext = provideSslContext();
Bootstrap b = new Bootstrap();
b.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new Channelinitializer(sslContext));
Channel ch1 = b.connect("localhost", 8008).sync().channel();
ch1.pipeline.get(SslHandler.class).handshakeFuture().sync()
Bootstrap b1 = new Bootstrap();
b1.group(workerGroup)
.channel(NioSocketChannel.class)
.handler(new Channelinitializer(sslContext));
Channel ch2 = b1.connect("localhost", 8009).sync().channel();

Related

Java netty tcp server with postgres. Query hangs indefinitely

I write Spring Boot application with tcp server on Netty. Service get messages and check rows in postgres database. The problem is that at the moment of checking the records in the database, the service hangs and stops processing other messages from the tcp channel.
Configuration:
#Bean
public void start() throws InterruptedException {
log.info("Starting server at: {} ", tcpPort);
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
b.group(workerGroup, bossGroup)
.channel(NioServerSocketChannel.class)
.childHandler(simpleTCPChannelInitializer)
.childOption(ChannelOption.SO_KEEPALIVE, true);
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(tcpPort).sync();
if(f.isSuccess())
log.info("Server started successfully");
f.channel().closeFuture().sync();
}
Channel initialization:
private final EventExecutorGroup sqlExecutorGroup = new DefaultEventExecutorGroup(16);
protected void initChannel(SocketChannel socketChannel) {
socketChannel.pipeline().addLast(new StringEncoder());
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(sqlExecutorGroup, simpleTCPChannelHandler);
}
and method for database:
#Override
public void processMessage(String atmRequest) {
log.info("Receive tcp atmRequest: {}", atmRequest);
checkDeviceInDatabase(deviceUid);
log.info("Receive power up command");
}
private void checkDeviceInDatabase(String deviceUid) {
statusConnectRepository.findById(deviceUid).orElseThrow(()
-> new DeviceNotFoundException("DeviceUid: " + deviceUid + " was not found in database"));
}
In checkDeviceInDatabase(deviceUid) method query hangs forever.
Has anyone met such a problem?

Netty Add Channel to ServerBootstrap

I have a ServerBootstrap accepting data from clients. Most of them are from any endpoint that connects to it, however I also want to handle data coming from a specific endpoint.
I'm reading and writing strings from n+1 connections basically. If the one specific connection ever closes, I would need to reopen it again.
Currently I'm trying to get a Bootstrap connected to the specific endpoint, and a ServerBootstrap handling all of the incoming connections, but the sync() that starts one of the Bootstraps blocks the rest of the application and I can't run the other one.
Or is it possible to just create a channel from scratch, connect to it, and add it to the EventLoopGroup?
Here's an example of what I have so far. Currently startServer() blocks at channelfuture.channel().closeFuture().sync()
private Channel mChannel;
private EventLoopGroup mListeningGroup;
private EventLoopGroup mSpeakingGroup;
public void startServer() {
try {
ServerBootstrap bootstrap = new ServerBootstrap()
.group(mListeningGroup, mSpeakingGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ServerInitializer());
ChannelFuture channelFuture = bootstrap.bind(mListeningPort).sync();
channelFuture.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mListeningGroup.shutdownGracefully();
mSpeakingGroup.shutdownGracefully();
}
}
public void startClient() throws InterruptedException {
Bootstrap bootstrap = new Bootstrap()
.group(mSpeakingGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ClientInitializer());
ChannelFuture future = bootstrap.connect(mAddress,mPort).sync();
mChannel = future.channel();
mChannel.closeFuture().addListener((ChannelFutureListener) futureListener -> mChannel = null).sync();
}
Once data is read by any of the n+1 sockets it puts it's message into a PriorityQueue and a while loops continuously pops off the queue and writes the data to every Channel. Does anyone have any ideas in regards to the best way to approach this?

Multiple Channels with Different Service Paths

[I am using Netty-Websokcet]
I have a use case where different service paths should be connected to the same port. I tried so many different ways, reasons I couldn't get the work done was,
In ServerBootstrap class there is only one place for ChannelHandler therefore I cannot add multiple child handlers in ServerBootstrap with different service paths
In ServerBootstrap class it is not possible to create multiple groups
This is how my init channel looks like,
#Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
logger.debug(1, "Initializing the SocketChannel : {}", socketChannel.id());
socketChannel.pipeline().addLast(
new HttpRequestDecoder(),
new HttpObjectAggregator(maxPayloadSize),
new HttpResponseEncoder(),
new IdleStateHandler(0, 0, listenerConfig.getSocketTimeout(),
TimeUnit.SECONDS),
new WebSocketServerProtocolHandler(ingressConfig.getURI().getPath()), // (A)
new WebSocketServerCompressionHandler(),
new WebSocketIO(listenerConfig, manager), // a handler
new WebSocketMessageListener(messageReceiver, manager) // a handler
);
logger.debug(2, "Successfully initialized the Socket Channel : {}", socketChannel.id());
}
This code line (A) registers a handler with the given service path (service path is ingressConfig.getURI().getPath())
int maxPayloadSize = listenerConfig.getMaxPayloadSize();
try {
bossGroup = new NioEventLoopGroup(listenerConfig.getBossThreadCount());
workerGroup = new NioEventLoopGroup(listenerConfig.getWorkerThreadCount());
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new WebSocketListenerInitializer(messageReceiver, maxPayloadSize, listenerConfig,
ingressConfig))
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture channelFuture = bootstrap.bind(port);
channelFuture.sync();
channel = channelFuture.channel();
if (channelFuture.isSuccess()) {
logger.info(1, "WebSocket listener started on port : {} successfully", port);
} else {
logger.error(2, "Failed to start WebSocket server on port : {}", port,
channelFuture.cause());
throw new TransportException("Failed to start WebSocket server", channelFuture.cause());
}
} catch (InterruptedException ex) {
logger.error(1, "Interrupted Exception from : {}", WebSocketListener.class);
throw new TransportException("Interrupted Exception", ex);
}
Can anyone suggest me a way how to do this?

Netty (4) client server communication in the same TCP Session

I need to build a client that initiates a TCP connection with a server and upon response it sends a hand shake request every 10 seconds and gets a response from the server. The server will be able to send another type of request which my client needs to read and act upon. I am using netty 4.0.26.Final.
I have built a client and a dummy server but I am facing an issue which possibly means that there is something I have not understood.
My Client:
String host = "localhost";
int port = 9884;
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new MyChannelPipeline());
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
String line = "line";
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (!line.equals("exit")) {
line = in.readLine();
if (line == null) {
break;
}
}
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
The ChannelPipleline:
#Override
public void initChannel(Channel ch) throws Exception {
ChannelPipeline channelPipeline = ch.pipeline();
//Encodes every request send from the client to the server
channelPipeline.addLast("clientRequestEncoder", new ClientRequestEncoder());
//Implements channelActive and exceptionCaught
channelPipeline.addLast("initialRequestHandler", new InitialRequestHandler());
channelPipeline.addLast("byteArrayDecoder", new ByteArrayDecoder());
channelPipeline.addLast("serverResponseDecoder", new ServerResponseDecoder());
channelPipeline.addLast("serverRequestDecoder", new ServerRequestDecoder());
//Reads the responses from the client requests AND
//reads the inbound requests from the server - Implements channelRead
//and exceptionCaught
channelPipeline.addLast("myResponseHandler", new MyResponseHandler());
}
The problem is that when I flush the response to the server (in MyResponseHandler) and exception is caught in InitialRequestHandler:
ERROR=java.lang.UnsupportedOperationException:unsupported message type: ServerResponse (expected: ByteBuf, FileRegion)
I don't see why the response is not flushed back to the server while the hand shake request is always properly flushed. In both write and flush I have used a ChannelFuture and onOperationComplete this listener f.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); is fired on failure.
Can I use two handlers in the same pipeline or is it bad practise? Moreover how should I fire an unregister event triggered by user input?
I solved this using one Handler that overrides channelActive and channelRead and I rearranged the encoders and decoders properly. I also solved the "unregister event triggered by user input" this way:
String line = "line";
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
do {
logger.info("You typed: " + line + ". Please type 'exit' to terminate the program!");
line = in.readLine();
} while (!line.equals("exit"));
logger.info("You typed: " + line + ". Please wait until the application is successfully shutdown...");
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
myChannelPipeline.getMyClientHandler().sendDisconnectRequest(future);
}
});
In sendDisconnectReqeust I send the final request and when I get the final response (in channelRead of MyHandler) I call disconnect on the pipeline:
ChannelPromise cp = new DefaultChannelPromise(ctx.channel());
ctx.channel().pipeline().disconnect(cp);
However I still have other issues with inbound requests that are never received by my client.

send large message between client and server with netty

I want to send large message between client and server with netty, but when I use netty for sending large message to server, In server I cannot get message complete for first time, in server I use ChannelHandlerAdapter when send large message from client method channelReadComplete run for two seconds, it must run for first time. Please see my client code and tell me my problem.
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch)
throws Exception {
ChannelPipeline p = ch.pipeline();
// if (sslCtx != null) {
// p.addLast(sslCtx.newHandler(ch.alloc(), HOST,
// PORT));
// }
System.out.println("initChannel-client");
p.addLast(new DiscardClientHandler(),
new LengthFieldBasedFrameDecoder(
100 * 1024, 0, 8));
}
});
// Make the connection attempt.
ChannelFuture f = b.connect(HOST, PORT).sync();
// // Wait until the connection is closed.
// // add by test
DiscardClient discardClient = new DiscardClient();
String message = discardClient.reafFile("D:\\log\\log1.txt");
ByteBuf encoded = f.channel().alloc().buffer(message.length());
encoded.writeBytes(message.getBytes());
f.channel().write(encoded);
f.channel().flush();
f.channel().closeFuture().sync();
} finally {
// group.shutdownGracefully();
}
Best Regards

Categories