1

I want to handle two different clients. One is simple tcp client which sends string packets. Another one is http client which sends httprequest msg. I am a beginner in Netty, I don't know how handlers in pipelines flow.

This is my server coding:

public class TCPServer {

    int port;

    public static void main(String[] args) {
        new TCPServer().start();
    }

    public void start() {
        port = 1222;
        EventLoopGroup producer = new NioEventLoopGroup();
        EventLoopGroup consumer = new NioEventLoopGroup();

        try {

            ServerBootstrap bootstrap = new ServerBootstrap()
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .group(producer, consumer)//separate event loop groups to handle for parent and child for handling all chanel events
                    .channel(NioServerSocketChannel.class)//select type of chanel
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ServerAdapterInitializer());//configure chanel pipeline
            System.out.println("Server started");// configuring server channel
            bootstrap.bind(port).sync().channel().closeFuture().sync();//start the server and Wait until the server socket is closed. Thread gets blocked. 

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            producer.shutdownGracefully();
            consumer.shutdownGracefully();
        }

    }

}

This is my serverInitializer:

<pre>public class ServerAdapterInitializer extends ChannelInitializer<SocketChannel> {//special chanel handler configures registered chanel pipeline

    @Override
    protected void initChannel(SocketChannel channel) throws Exception {//this method is called once the chanel was registered
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast("decoder", new StringDecoder());//chanel inbound handler
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new TCPServerHandler());

    }

}

And this my handler to handle both httprequest and string. But my handler never handle httprequest packet.

class TCPServerHandler extends SimpleChannelInboundHandler<Object> {
    private static final byte[] CONTENT = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };

    private static final ChannelGroup channels = new DefaultChannelGroup("tasks", GlobalEventExecutor.INSTANCE);                                                               

    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg)
            throws Exception {       

         if (msg instanceof HttpRequest) {
             System.out.println("http request");
            HttpRequest req = (HttpRequest) msg;

            boolean keepAlive = HttpUtil.isKeepAlive(req);
            FullHttpResponse response = new DefaultFullHttpResponse(req.protocolVersion(), OK,Unpooled.wrappedBuffer(CONTENT));
            response.headers()
                    .set(CONTENT_TYPE, TEXT_PLAIN)
                    .setInt(CONTENT_LENGTH, response.content().readableBytes());

            if (keepAlive) {
                if (!req.protocolVersion().isKeepAliveDefault()) {
                    response.headers().set(CONNECTION, KEEP_ALIVE);
                }
            } else {
                // Tell the client we're going to close the connection.
                response.headers().set(CONNECTION, CLOSE);
            }

            ChannelFuture f = ctx.write(response);

            if (!keepAlive) {
                f.addListener(ChannelFutureListener.CLOSE);
            }
        }

          if(msg instanceof String){
               System.out.println("String request");
            String arg1=(String)msg;
            Channel currentChannel = ctx.channel();
        if(arg1.equals("quit")){
            System.out.println("[INFO] - " + currentChannel.remoteAddress() + " is quitting... ");
        }else{
        System.out.println("[INFO] - " + currentChannel.remoteAddress() + " - "+ arg1);
        currentChannel.writeAndFlush("Server Said Hii "+ arg1);
        }
        }
    }

}
Roman Patutin
  • 2,171
  • 4
  • 23
  • 27

1 Answers1

0

I don't think it is possible to configure the same server bootstrap to handle both HTTP requests and raw String messages. You need two server bootstraps (one for HTTP, one for String messages) each with its own pipeline. You already have the decoder/encoder for String message handling.

EventLoopGroup producer = new NioEventLoopGroup();
EventLoopGroup consumer = new NioEventLoopGroup();
ServerBootstrap httpSb = new ServerBootstrap();
ServerBootstrap strSb  = new ServerBootstrap();

httpSb.group(producer, consumer).bind(<port for http>).<other methods>...
strSb.group(producer, consumer).bind(<port for strings>).<other methods>...

For HTTP, you need to add handlers HttpServerCodec and HttpObjectAggregator to be able to read FullHttpRequest from the channel and write FullHttpResponse into the channel.

(aggregator is optional, it helps you to avoid the task of combining fragmented incoming HTTP data into a single (full) HTTP request as well as write a combined (full) HTTP response into the channel)

In bootstrap for HTTP:

ch.pipeline().addLast("httpcodec"     , new HttpServerCodec());
ch.pipeline().addLast("httpaggregator", new HttpObjectAggregator(512 * 1024));
ch.pipeline().addLast("yourhandler"   , new YourHttpRequestHandler());

Example handler for FullHttpRequest processing:

public class YourHttpRequestHandler extends ChannelInboundHandlerAdapter  {


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg_arg) 
    {
            FullHttpRequest msg = (FullHttpRequest)msg_arg;

            System.out.println("URI: " + msg.getUri());
            System.out.println("method: " + msg.getMethod().toString());
            System.out.println("protocol version: " + msg.getProtocolVersion()); 
            System.out.println("header1: " + msg.headers().get("header1"));
            System.out.println("header2: " + msg.headers().get("header2"));
            System.out.println("header3: " + msg.headers().get("header3"));
            System.out.println("content: " + msg.content().toString(CharsetUtil.UTF_8));

    }//end read

}//end handler
mangusta
  • 3,470
  • 5
  • 24
  • 47
  • Thank You so much. I have one more doubt. Is only one Inbound Outbound handler and one userhandler is allowed for one server pipeline – Nabishathul Missriya Dec 21 '19 at 14:56
  • no, you can use as many handlers as you wish. (and there is no such thing as "userhandler". decoders are inbound handlers, encoders are outbound handlers) – mangusta Dec 21 '19 at 14:58
  • Sorry. I am saying about my own handler. Am I able to add two handlers? and how the flow will be like. Could you please explain me about this – Nabishathul Missriya Dec 21 '19 at 15:06
  • the incoming message will pass through inbound handlers, from left to right, until you perform outbound operation (for example, write). after that, your outbound handlers will be called from right to left, until the message leaves the pipeline. but there are some important details that are better explained in the Netty tutorials. make sure to read it carefully. ("left to right" is the order that you add your handlers into pipeline) – mangusta Dec 21 '19 at 15:10
  • Thank You. Please tell me about two handlers which i asked before – Nabishathul Missriya Dec 21 '19 at 15:22
  • @NabishathulMissriya in case of String messages the flow is: `StringDecoder -> TCPServerHandler -> StringEncoder`. In case of HTTP, the flow is: `HttpServerCodec -> HttpObjectAggregator -> YourHttpRequestHandler -> HttpObjectAggregator -> HttpServerCodec` – mangusta Dec 21 '19 at 15:25
  • @NabishathulMissriya you're welcome, please accept the answer if it was helpful) – mangusta Dec 21 '19 at 15:32
  • On running the server it only binds to the first port(HttpServer port). It doesn't binds with second port(Raw String server port). What should i do to process both inputs. – Nabishathul Missriya Dec 21 '19 at 17:30
  • @NabishathulMissriya did you use two different port numbers? – mangusta Dec 21 '19 at 17:52
  • httpSb.group(producer, consumer) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new HttpServerInitializer(sslCtx)); httpSb.bind(hPORT).sync().channel().closeFuture().sync(); – Nabishathul Missriya Dec 21 '19 at 18:05
  • strSb.option(ChannelOption.SO_BACKLOG, 1024) .group(producer, consumer) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new StringServerInitializer()); strSb.bind(sPORT).sync().channel().closeFuture().sync(); – Nabishathul Missriya Dec 21 '19 at 18:06
  • In the above hPORT is httpserver port and sPORT is raw string server port – Nabishathul Missriya Dec 21 '19 at 18:07
  • @NabishathulMissriya this part `.channel().closeFuture().sync();` blocks current thread, so it can't proceed to the next line, that's why `strSb` server cannot get started. have a look at this page, it explains how to start multiple bootstraps correctly: https://stackoverflow.com/questions/27112084/how-to-use-multiple-serverbootstrap-objects-in-netty – mangusta Dec 21 '19 at 18:08
  • I visited that page. Thank You for your reference. – Nabishathul Missriya Dec 21 '19 at 18:47