Netty encoder not executed - java

I made a server with Netty but I'm having a problem. The encoder that i created is not being executed.
My pipeline on the server:
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("encoder", new PacketEncoder());
ch.pipeline().addLast("decoder", new PacketDecoder());
ch.pipeline().addLast(new LoginServerChannel());
}
});
My encoder:
public class PacketEncoder extends MessageToByteEncoder<Packet> {
#Override
protected void encode(ChannelHandlerContext channelHandlerContext, Packet packet, ByteBuf byteBuf) throws Exception {
System.out.println("Encoding");
}
My decoder:
public class PacketDecoder extends ReplayingDecoder<DecoderState> {
#Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
...
}
My channel handler:
public class LoginServerChannel extends SimpleChannelInboundHandler<Packet> {
#Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Packet packet) throws Exception {
channelHandlerContext.writeAndFlush(packet.encode());
System.out.println("Sending");
}
The decoder is called and then the channel handler but not the encoder. I have tried to change the order in the pipeline but same problem also try to used fireChannelRead(...) and same problem.
Thanks

Your encoder extends MessageToByteEncoder<Packet> so it is called when a Packet is received in input to encode it.
In your logic handler, you do
channelHandlerContext.writeAndFlush( packet.encode() );
I suppose that encode() returns a byte array, so your encoder ignore it and do nothing.
You probably should do something like that:
channelHandlerContext.writeAndFlush( packet );

Related

Netty chunked input stream

I have seen lots of questions around about chunked streams in netty, but most of them were solutions about outbound streams, not inbound streams.
I would like to understand how can I get the data from the channel and send it as an InputStream to my business logic without loading all the data in memory first.
Here's what I was trying to do:
public class ServerRequestHandler extends MessageToMessageDecoder<HttpObject> {
private HttpServletRequest request;
private PipedOutputStream os;
private PipedInputStream is;
#Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
this.os = new PipedOutputStream();
this.is = new PipedInputStream(os);
}
#Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
this.os.close();
this.is.close();
}
#Override
protected void decode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out)
throws Exception {
if (msg instanceof HttpRequest) {
this.request = new CustomHttpRequest((HttpRequest) msg, this.is);
out.add(this.request);
}
if (msg instanceof HttpContent) {
ByteBuf body = ((HttpContent) msg).content();
if (body.readableBytes() > 0)
body.readBytes(os, body.readableBytes());
if (msg instanceof LastHttpContent) {
os.close();
}
}
}
}
And then I have another Handler that will get my CustomHttpRequest and send to what I call a ServiceHandler, where my business logic will read from the InputStream.
public class ServiceRouterHandler extends SimpleChannelInboundHandler<CustomHttpRequest> {
...
#Override
public void channelRead0(ChannelHandlerContext ctx, CustomHttpRequest request) throws IOException {
...
future = serviceHandler.handle(request, response);
...
This does not work because when my Handler forwards the CustomHttpRequest to the ServiceHandler, and it tries to read from the InputStream, the thread is blocking, and the HttpContent is never handled in my Decoder.
I know I can try to create a separate thread for my Business Logic, but I have the impression I am overcomplicating things here.
I looked at ByteBufInputStream, but it says that
Please note that it only reads up to the number of readable bytes
determined at the moment of construction.
So I don't think it will work for Chunked Http requests. Also, I saw ChunkedWriteHandler, which seems fine for Oubound chunks, but I couldn't find something as ChunkedReadHandler...
So my question is: what's the best way to do this? My requirementes are:
- Do not keep data in memory before sending the ServiceHandlers;
- The ServiceHandlers API should be netty agnostic (that's why I use my CustomHttpRequest, instead of Netty's HttpRequest);
UPDATE
I have got this to work using a more reactive approach on the CustomHttpRequest. Now, the request does not provide an InputStream to the ServiceHandlers so they can read (which was blocking), but instead, the CustomHttpRequest now has a readInto(OutputStream) method that returns a Future, and all the service handler will just be executed when this Outputstream is fullfilled. Here is how it looks like
public class CustomHttpRequest {
...constructors and other methods hidden...
private final SettableFuture<Void> writeCompleteFuture = SettableFuture.create();
private final SettableFuture<OutputStream> outputStreamFuture = SettableFuture.create();
private ListenableFuture<Void> lastWriteFuture = Futures.transform(outputStreamFuture, x-> null);
public ListenableFuture<Void> readInto(OutputStream os) throws IOException {
outputStreamFuture.set(os);
return this.writeCompleteFuture;
}
ListenableFuture<Void> writeChunk(byte[] buf) {
this.lastWriteFuture = Futures.transform(lastWriteFuture, (AsyncFunction<Void, Void>) (os) -> {
outputStreamFuture.get().write(buf);
return Futures.immediateFuture(null);
});
return lastWriteFuture;
}
void complete() {
ListenableFuture<Void> future =
Futures.transform(lastWriteFuture, (AsyncFunction<Void, Void>) x -> {
outputStreamFuture.get().close();
return Futures.immediateFuture(null);
});
addFinallyCallback(future, () -> {
this.writeCompleteFuture.set(null);
});
}
}
And my updated ServletRequestHandler looks like this:
public class ServerRequestHandler extends MessageToMessageDecoder<HttpObject> {
private NettyHttpServletRequestAdaptor request;
#Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
super.handlerAdded(ctx);
}
#Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
}
#Override
protected void decode(ChannelHandlerContext ctx, HttpObject msg, List<Object> out)
throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest request = (HttpRequest) msg;
this.request = new CustomHttpRequest(request, ctx.channel());
out.add(this.request);
}
if (msg instanceof HttpContent) {
ByteBuf buf = ((HttpContent) msg).content();
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
this.request.writeChunk(bytes);
if (msg instanceof LastHttpContent) {
this.request.complete();
}
}
}
}
This works pretty well, but still, note that everything here is done in a single thread, and maybe for large data I might want to spawn a new thread to release that thread for other channels.
You're on the right track - if your serviceHandler.handle(request, response); call is doing a blocking read, you need to create a new thread for it. Remember, there are supposed to be only a small number of Netty worker threads, so you shouldn't do any blocking calls in worker threads.
The other question to ask is, does your service handler need to be blocking? What does it do? If it's shoveling the data over the network anyway, can you incorporate it into the Netty pipeline in a non-blocking way? That way, everything is async all the way, no blocking calls and extra threads required.

Core game logic loop to send data to channels in Netty

I want to send world state every 100 msec to all channels. But it calls only once.
My code:
public class IncomeMessageTcpHandler extends SimpleChannelInboundHandler<byte[]> {
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channel.eventLoop().scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
System.out.println("send");
channel.writeAndFlush(GameLogic.Instance.flushMessageData());
}
}, 100, 100, TimeUnit.MILLISECONDS);
}
}
Now the method call only once.
I use 4.1.13.Final
public class UnityServerTcpChannelInitializer extends ChannelInitializer<SocketChannel> {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast("frameDecoder", new FixedLengthFrameDecoder(6));
p.addLast("bytesDecoder", new ByteArrayDecoder());
p.addLast("bytesEncoder", new ByteArrayEncoder());
p.addLast(new IncomeMessageTcpHandler());
}
}
When I comment channel.writeAndFlush(GameLogic.Instance.flushMessageData()); task run every 100 msec
Omg... my fault.
My method GameLogic.Instance.flushMessageData() generates NullPointerException but I did not know that Runnable do not throw any exception.
That's why it stops running without any warn

java netty as tcpServer,delphi TIdTCPClient as tcpClient

I use the java netty as tcpServer and delphi TIdTCPClient as tcpClient.the tcpclient can connect and send message to the tcpserver but the tcpclinet can not receive the message sendback from the tcpserver .
here is the tcpserver code written by java:
public class NettyServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new TcpServerHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public class TcpServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException {
try {
ByteBuf in = (ByteBuf) msg;
System.out.println("channelRead:" + in.toString(CharsetUtil.UTF_8));
byte[] responseByteArray = "hello".getBytes("UTF-8");
ByteBuf out = ctx.alloc().buffer(responseByteArray.length);
out.writeBytes(responseByteArray);
ctx.writeAndFlush(out);
//ctx.write("hello");
} finally {
ReferenceCountUtil.release(msg);
}
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws UnsupportedEncodingException{
System.out.println("channelActive:" + ctx.channel().remoteAddress());
ChannelGroups.add(ctx.channel());
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("channelInactive:" + ctx.channel().remoteAddress());
ChannelGroups.discard(ctx.channel());
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
here is the tcpclient code written by delphi :
AStream := TStringStream.Create;
IdTCPClient.IOHandler.ReadStream(AStream);
i also use the
IdTCPClient.IOHandler.ReadLn()
and still can not get the returnDATA.
Your Delphi code does not match your Java code, that is why your client is not working.
The default parameters of TIdIOHandler.ReadStream() expect the stream data to be preceeded by the stream length, in bytes, using either a 32bit or 64bit integer in network byte order (big endian) depending on the value of the TIdIOHandler.LargeStream property. Your Java code is not sending the array length before sending the array bytes.
The default parameters of TIdIOHandler.ReadLn() expect the line data to be terminated by either a CRLF or bare-LN terminator. Your Java code is not sending any line terminator at the end of the array bytes.
In short, your Java code is not sending anything that lets the receiver know when the sent data actually ends. Unless it closes the connection after sending the data, in which case you can set the AReadUntilDisconnect parameter of TIdIOHandler.ReadStream() to true, or use TIdIOHandler.AllData().
TCP is stream-oriented, not message-oriented. The sender must be explicit about where a message ends and the next message begins.

Multiple ChannelInboundHandlerAdapter's in Netty pipleline

I'm quite new with netty, I want to create a TCP server which does a custom application layer handshaking when a connection is to be instantiated. After the handshaking I want to pass the messages (ByteBuf) to a queue so that they could be processed by some other threads.
My question is, can I have multiple ChannelInboundHandlerAdapter's in the channel pipeline? one for the application layer handshaking protocol and the other one for passing the message to the queue. Furthermore I want to know how the messages flow through the pipeline. If a message is received at one handler (or decoder/encoder) how is it passed to another handler.
Specifically, if I change the EchoServer from here and add another ChannelInboundHandlerAdapter, the echo server handler would stop receiving any messages.
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch)
throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
#Override
public void channelRead(ChannelHandlerContext ctx,
Object msg) {
}
});
ch.pipeline().addLast(
new EchoServerHandler());
}
});
My logic is: have 2 ChannelInboundHandlerAdapter's then do the handshaking with the first handler and discard packets if they do not match the handshaking criteria, and then pass the messages to a queue through the second ChannelInboundHandlerAdapter. Is my logic correct? If not how should it be?
Thank you very much.
ChannelInboundHandlerAdapter is an adapter class to the ChannelInBoundHandler interface. For beginning you can use SimpleChannelInboundHandler (or more complicated you can extend the adapter class writing your own handler that extends ChannelInboundHandlerAdapter ).
The SimpleCHannelInboundHandler releases the message automatically after channelRead() (and thereby passes it to the next handler in the ChannelPipeline).
For using the easier SimpleChannelInboundHandler see this thread Netty hello world example not working
So instead of this ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {}
you have to write a new class that extends SimpleChannelInboundHandler like
public class MyHandler extends SimpleChannelInboundHandler{
#Override
protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
try {
System.out.println(in.toString(io.netty.util.CharsetUtil.US_ASCII));
} finally {
in.release();
}
}
}
and invoke it like
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new MyHandler());
}
As said above the SimpleCHannelInboundHandler releases the message automatically after channelRead() (and thereby passes it to the next handler in the ChannelPipeline).
If you use ChannelInboundHandlerAdapter you have to implement the passing of the message/event to the next handler yourself
A handler has to invoke the event propagation methods in ChannelHandlerContext ctx to forward an event to its next handler. (in the SimpleChannelInboundHandler class this is implemented yet)
public class MyInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("Connected!");
ctx.fireChannelActive();
}
}
See this http://netty.io/4.0/api/io/netty/channel/ChannelPipeline.html
I must remind that:
Only One SimpleChannelInboundHandler extention can be add to the pipeline chain.
Because SimpleChannelInboundHandler have a finally code block will release all the msg.
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
if (acceptInboundMessage(msg)) {
#SuppressWarnings("unchecked")
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
release = false;
ctx.fireChannelRead(msg);
}
} finally {
if (autoRelease && release) {
//release all handled messages,so the next handler won't be executed
ReferenceCountUtil.release(msg);**
}
}
}
Use ChannelInboundHandlerAdapter instead:
public class CustomizeChannelInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("do something you like!")
super.channelRead(ctx, msg);
}
}

Netty: Make use of a pooled buffer for writing

I'm currently working on a POC using Netty and till so far it goes very nice and managed to get quite some functionality up and running.
I have a question however about reusing the byte-buffer for writing. In the following example you can see a manually created bytebuffer-response, but it is created for every request and that isn't needed. I would like to make use of 'buf'. I'm currently running a bit in the trial and error mode and I have checked out the examples. Although my case looks very standard, I have not been able to figure out the correct way of making of a pooled buffer.
public class OperationHandler extends ChannelInboundHandlerAdapter {
private ByteBuf buf;
#Override
public void handlerAdded(ChannelHandlerContext ctx) {
buf = ctx.alloc().buffer(1024);
// System.out.println("Channel handler added");
}
#Override
public void handlerRemoved(ChannelHandlerContext ctx) {
buf.release();
buf = null;
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
try {
ByteBuffer response = ByteBuffer.allocate(1024);
byte[] operation = (byte[]) msg;
invoker.invoke(operation, response);
response.flip();
ctx.write(Unpooled.wrappedBuffer(response));
} finally {
ReferenceCountUtil.release(msg);
}
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}

Categories

Resources