I can not figure out how to identify the users, because the ChannelHandlerContext is not the same in the LoggingHandler and the SimpleChannelInboundHandler:
class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec());
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WebSocketServerProtocolHandler(WEBSOCKET_PATH, null, true));
pipeline.addLast(new WebSocketIndexPageHandler(WEBSOCKET_PATH));
pipeline.addLast(new WebSocketFrameHandler());
pipeline.addLast(new SessionManagerAxx());
}
}
LoggingHandler :
class SessionManagerAxx extends LoggingHandler {
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt.equals(ServerHandshakeStateEvent.HANDSHAKE_COMPLETE)) {
///add the user
Clients.getInstance().addNewClient(ctx);
}
super.userEventTriggered(ctx, evt);
}
}
FrameHandler:
class WebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
private static final Logger logger = LoggerFactory.getLogger(WebSocketFrameHandler.class);
#Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
Clients.getInstance().getClient(ctx);
//Handle user msg
}
}
You could add something to Channel.attr(...) which you then use in both handlers.
Authentication is described in "State Management" section of ChannelHandler javadoc
It seems that the nodes in the pipeline do not share the ChannelHandlerContext
So I solved the problem joining the FrameHandler and the SessionManager in a single class.
Related
I am trying to setup a request/response client ChannelHandler. At this point I'm able to get the DefaultHttpResponse in my channelRead0 method, but I don't know how to get the DefaultFullHttpResponse.
The reason I need the DefaultFullHttpResponse is that the DefaultHttpResponse doesn't appear to contain the response body returned from the server.
My ChannelHander-
public class NettyClientHandler extends SimpleChannelInboundHandler<DefaultHttpResponse> {
private ChannelHandlerContext ctx;
private BlockingQueue<Promise<DefaultHttpResponse>> messageList = new LinkedBlockingQueue<>(1_000_000);
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
this.ctx = ctx;
}
#Override
public void channelRead0(ChannelHandlerContext ctx, DefaultHttpResponse msg) {
synchronized (this) {
messageList.poll().setSuccess(msg);
System.out.println(msg);
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx)
throws Exception {
super.channelReadComplete(ctx);
System.out.println("channelReadComplete");
}
}
And how the channel pipeline is configured in the ChannelPoolHandler-
#Override
public void channelCreated(Channel channel) throws Exception {
channel.pipeline().addLast(sslContext.newHandler(channel.alloc()));
channel.pipeline().addLast(new HttpClientCodec());
channel.pipeline().addLast(new HttpContentDecompressor());
channel.pipeline().addLast(new NettyClientHandler());
}
I looked around but I couldn't find any FullHttpClientCodec or options in the HttpClientCodec that would allow me to do this.
How can I get the DefaultFullHttpResponse to be passed to the channelRead0 method?
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
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);
}
}
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 );
I wanna make something like a simple chat.
Server have to add new clients in a list of clients, and when one client sends message to the server, server have to resend this message to the other clients.
I know, how to read message from client, but I don't know, how to send message from server to the client. And i don't sure where should be list of clients, but guess that in handler class.
Here is my main class which initializes the server class
package firstPackage;
public class main {
public static void main(String[] args) throws Exception
{
Server server = new Server(9050);
server.run();
}
}
Here is the Server class
package firstPackage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
public class Server {
private int port;
public Server(int port)
{
this.port=port;
}
public void run() throws Exception
{
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 DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
}
finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
And here the Handler class
package firstPackage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) {
System.out.print((char) in.readByte());
System.out.flush();
}
System.out.println();
ctx.writeAndFlush("hey"); // вот здесь я думал, что сообщение будет отправлятся клиенту, от которого я получил сообщение, но не отправляется
} finally {
ReferenceCountUtil.release(msg);
}
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel is active");
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channel is invactive");
}
#Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
System.out.println("handler added");
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
Actually now I have not list of clients, because i even don't know what type of object this list must contain, in C# it's was Socket object, so what in Netty?
I'm betting this example should help you out (it's a YouTube video, so spare the comments about broken links please). In particular, it uses a ChannelGroup to solve the problem of which you ask. And, yes, it goes in the handler on the server side.
Edit:
Notice too that in the example the ChannelGroup is static. I might argue it would be better to not use a static member and inject a ChannelGroup into the handler from the server class, but the simplicity of the static member may be preferable if you just want to get something working quickly.