Frage

As part of my protocol, I'd like the client to send its version number when a new connection is established. I'd like this to be done in a separate handler in the pipeline, so please bear with me as this may be a fairly basic question but I'm not sure how to do it. The other thing is that I'd like to then be able to send POJO's back and forth through the connection (pipeline). Also I'd love to add an Authentication handler. Anyways, right now I'm getting some kind of error and I'm pretty sure it's because the version check is not being properly digested from the pipeline.

Basically the code I have below is setup to send "Hello World" which the server prints out after the version has been checked when the connection has been established. At least in theory, in reality this isn't quite working ;)

Currently I have:

Client.java

public static void main(String[] args)
{
    ...

    // Set up the pipeline factory.
    bootstrap.setPipelineFactory(new ChannelPipelineFactory() 
    {
        @Override
        public ChannelPipeline getPipeline() throws Exception 
        {
            return Channels.pipeline(
                    new ObjectEncoder(),
                    new ObjectDecoder(),
                    new VersionClientHandler(),
                    new BusinessLogicClientHandler());
        }
    });     

    ...

    // The idea is that it will all be request and response. Much like http but with pojo's. 
    ChannelFuture lastWriteFuture = channel.write("Hello world".getBytes());

    if (lastWriteFuture != null) 
    {
        System.out.println("waiting for message to be sent");
           lastWriteFuture.awaitUninterruptibly();
    }

    ...
}

VersionClientHandler.java

public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
{
    ChannelBuffer versionBuffer = ChannelBuffers.buffer(VERSION_STRING_LENGTH);
    versionBuffer.writeBytes("v123.45a".getBytes());
    // If I understand correctly, the next line says use the rest of the stream to do what you need to the next Handler in the pipeline?
    Channels.write(ctx, e.getFuture(), versionBuffer);  
}

BusinessLogicClientHandler.java

Not really doing anything at this point. Should it?

Server.java

public static void main(String[] args)
{
    ...

    public ChannelPipeline getPipeline() throws Exception 
    {
        return Channels.pipeline(
                new ObjectEncoder(),
                new ObjectDecoder(),
                new VersionServerHandler(),
                new BusinessLogicServerHandler());
    }

    ...
}

VersionServerHandler.java

public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 
{
    ChannelBuffer versionBuffer = ChannelBuffers.buffer(VERSION_NUMBER_MAX_SIZE);
    System.out.println("isReadable - messageReceived: " + versionBuffer.readable()); // returns false???
    // Basically I want to read it and confirm the client and server versions match.
    // And if the match fails either send a message or throw an exception
    // How do I also pass on the stream to the next Handler? 
}

BusinessLogicServerHandler.java

public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 
{
    e.getMessage();
    byte[] message = (byte[])e.getMessage(); // "Hello World" in byte[] from Client.java
}

So basically what I want is that the version number is passed and validated when the channel is connected as part of the communication protocol. All done automatically behind the scenes. Similarly I'd love to pass the authentication mechanism this way.

I did see some code which looked somewhat like what I wanted to do with the Secure Chat example, but I couldn't really figure it out. Any help on how to setup this code would be really appreciated. I know I could do it all in one massive handler, but that's the point of the pipeline, to break it up into units that make logical sense.

War es hilfreich?

Lösung

I found the solution!!!

There were a number of issues.

On VersionClientHandler, the new code is:

public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
{
    String verison = "v123.45a";

    ChannelBuffer versionBuffer = ChannelBuffers.buffer(VERSION_STRING_LENGTH);
    versionBuffer.writeBytes(version.getBytes());
    e.getChannel().write(version);
}

Notice the last line, e.getChannel().write(version); instead of Channels.write(ctx, e.getFuture(), versionBuffer); I'm not sure of the why. In fact, I'm about to start looking into why I have the ChannelBuffers code there because it doesn't seem to do anything...

On VersionServerHandler.java I now have:

public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 
{
    String versionCheck = (String)e.getMessage();
    System.out.println("VersionServerHandler - " + versionCheck);
    ctx.getPipeline().remove(VersionServerHandler.class);
}

Notice I don't read through the buffer anymore, I just do e.getMessage() and cast to the correct type of object. Above this I added ctx.getPipeline().remove(VersionServerHandler.class); which is there to remove that handler from any further processing. It's no longer required after the initial connection. Thanks to Dennis for that tip.

Conclusion

The rest is much as I expected. the key is that I wasn't correctly understanding how to read the buffers and pass the info around. And the error messages and examples weren't quite clear. As soon as you add the POJO channels from Netty to your pipeline, you need to start dealing only in objects, for all handlers. I missed that one. The concepts were right, it's just how I tried to read the data from the channels that was wrong.

The other big tip, was to remove the handlers from the pipeline if you don't need them after the initial connection. I assume the same will be true for an authentication handler. It would be great to have confirmation here, but I'll have to figure that one out later.

Andere Tipps

You didn't mention what the error was that you are seeing.

In any case, I would not recommend using a separate channel handler for doing a version check. Mainly because the version check should only need to happen once, when the connection is first established. Also because I think the channel handlers are best left dealing with transport layer concerns e.g. converting bytes into pojos.

I created a simple example: https://github.com/boldt/netty-examples/

It returns the version number when a new connection is established. It is done in a separate handler which will be removed from the pipeline after the version is written on the channel. Afterwards it is the ECHO example from the netty tutorial.

A telnet localhost 1234 to the server shows the version immediately.

Like this you could add an authentication-handler to the pipeline behind the version-handler.

I think what you want todo can be archived easily by adding some custom handlers. So for the version check you could add a handler that override channelConnected(....) and do rhe check there. For auth just add another handler after the version check which override the messageRecieved(....) method. After the auth is complete you can remove the handler frm the pipeline and add it back once you need it again.

The BusinessLogic Handler should sit in the pipeline as the last one. Just be aware that any of your handlers does some blocking operation you should think about adding a ExecutionHandler in front of it to mKe sure hat the ioworker thread will nit get blocked and so make the netty server unresponsible.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top