简体   繁体   中英

what is the correct usage for StringEncoder and StringDecoder with a netty server?

Is the string based server, below, functionally equivalent to its datagram server brethren?

The only notable difference I see, and the only difference I'm trying to achieve, is to go from NioDatagramChannel to nioServerSocketChannel . There are some minor differences between the handlers, but do both handlers respond to "QOTM" with nextQuote() ?

For brevity, and sanity, I cannot include the client. I'm unfamiliar with netty, and there's simply not much 4.x documentation on this topic I can find. Netty in Action does say:

7.2.4 MessageToMessageDecoder – Decode POJO's on the fly If you want to decode a message to another type of message MessageToMessageDecoder is the way to go. It allows an easy way to do so. The semantic is quite the same as for all the other decoders we explained before.

But for simplicity I'm just trying to utilize String en/de-coders for the time being. Am I using them correctly in the server?

see also:

http://seeallhearall.blogspot.ca/2012/05/netty-tutorial-part-1-introduction-to.html

datagram server:

package net.bounceme.dur.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import java.util.logging.Logger;
import net.bounceme.dur.netty.datagram.DatagramServerInitializer;

public final class DatagramServer {

    private static final Logger log = Logger.getLogger(DatagramServer.class.getName());

    public void start() throws InterruptedException {
        MyProps p = new MyProps();
        int port = p.getServerPort();
        pingPong(port);
    }


    private void pingPong(int port) throws InterruptedException {
        log.fine("which handler?");
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioDatagramChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new DatagramServerInitializer());
            b.bind(port).sync().channel().closeFuture().await();
        } finally {
            group.shutdownGracefully();
        }
    }
}   

string server:

package net.bounceme.dur.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.util.logging.Logger;
import net.bounceme.dur.netty.string.StringServerInitializer;

public final class StringServer {

    private static final Logger log = Logger.getLogger(StringServer.class.getName());

    public void start() throws InterruptedException {
        MyProps p = new MyProps();
        int port = p.getServerPort();
        pingPong(port);
    }

    private void pingPong(int port) throws InterruptedException {
        log.fine("which handler?");
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BROADCAST, true)
                    .handler(new StringServerInitializer());
            b.bind(port).sync().channel().closeFuture().await();
        } finally {
            group.shutdownGracefully();
        }
    }
}

datagram server handler:

package net.bounceme.dur.netty.datagram;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.socket.DatagramPacket;
import io.netty.util.CharsetUtil;
import java.util.Random;
import java.util.logging.Logger;

public class DatagramServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

    private static final Logger log = Logger.getLogger(DatagramServerHandler.class.getName());
    private static final Random random = new Random();

    public DatagramServerHandler() {
        log.info("..started..");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        ctx.writeAndFlush(nextQuote());
    }

    // Quotes from Mohandas K. Gandhi:
    private static final String[] quotes = {
        "Where there is love there is life.",
        "First they ignore you, then they laugh at you, then they fight you, then you win.",
        "Be the change you want to see in the world.",
        "The weak can never forgive. Forgiveness is the attribute of the strong.",};

    private static String nextQuote() {
        int quoteId;
        synchronized (random) {
            quoteId = random.nextInt(quotes.length);
        }
        return quotes[quoteId];
    }

    @Override
    public void channelRead0(ChannelHandlerContext ctx, DatagramPacket packet) throws Exception {
        log.info(packet.toString());
        if ("QOTM?".equals(packet.content().toString(CharsetUtil.UTF_8))) {
            ctx.write(new DatagramPacket(
                    Unpooled.copiedBuffer("QOTM: " + nextQuote(), CharsetUtil.UTF_8), packet.sender()));
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.severe(cause.toString());
    }
}

datagram server initializer:

package net.bounceme.dur.netty.datagram;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import java.util.logging.Logger;

public class DatagramServerInitializer extends ChannelInitializer<NioDatagramChannel> {

    private static final Logger log = Logger.getLogger(DatagramServerInitializer.class.getName());

    public DatagramServerInitializer() {
        log.info("..initializing..");
    }

    @Override
    protected void initChannel(NioDatagramChannel c) throws Exception {
        log.info("..adding to pipeline..");
        ChannelPipeline pipeline = c.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast(new DatagramServerHandler());
    }
}

string server handler:

package net.bounceme.dur.netty.string;

import net.bounceme.dur.netty.datagram.DatagramServerHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Random;
import java.util.logging.Logger;

public class StringServerHandler extends SimpleChannelInboundHandler<String> {

    private static final Logger log = Logger.getLogger(DatagramServerHandler.class.getName());
    private static final Random random = new Random();

    public StringServerHandler() {
        log.info("..started..");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        ctx.writeAndFlush(nextQuote());
    }

    // Quotes from Mohandas K. Gandhi:
    private static final String[] quotes = {
        "Where there is love there is life.",
        "First they ignore you, then they laugh at you, then they fight you, then you win.",
        "Be the change you want to see in the world.",
        "The weak can never forgive. Forgiveness is the attribute of the strong.",};

    private static String nextQuote() {
        int quoteId;
        synchronized (random) {
            quoteId = random.nextInt(quotes.length);
        }
        return quotes[quoteId];
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.severe(cause.toString());
    }

    @Override
    protected void channelRead0(ChannelHandlerContext chc, String msg) throws Exception {
        System.err.println(msg);
        if ("QOTM?".equals(msg)) {
            chc.writeAndFlush(nextQuote());
        } else {
            log.warning(msg);  //never executes
        }
    }
}

string server initializer:

package net.bounceme.dur.netty.string;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.logging.Logger;

public class StringServerInitializer extends ChannelInitializer<ServerSocketChannel> {

    private static final Logger log = Logger.getLogger(StringServerInitializer.class.getName());

    public StringServerInitializer() {
        log.info("..initializing..");
    }

    @Override
    protected void initChannel(ServerSocketChannel c) throws Exception {
        log.info("..adding to pipeline..");
        ChannelPipeline pipeline = c.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
        pipeline.addLast(new StringDecoder());
        pipeline.addLast(new StringEncoder());
        pipeline.addLast(new StringServerHandler());
    }
}

You do appear to be using the string encoders correctly in the TCP version.

However TCP and UDP are not the same. With a TCP connection the first incoming handler in your pipeline will receive a buffer containing data read from the socket buffers. The socket (and therefore channel) is bound to a socket on the remote peer and therefore the channel contains all the information required to communicate with the remote peer. This includes the remote address and port. Netty may also create a separate pipeline instance attached to that channel (depends on how you set it up).

Although the API is similar, UDP acts very differently. It's not a stream, but a set of discrete datagram packets and a single channel will receive datagram packets from many different senders. The first incoming handler in your pipeline will receive a DatagramPacket containing at least your payload (your message) and the sender. DelimiterBasedFrameDecoder cannot work on a DatagramPacket - it expects a ByteBuf and is designed to work with streams where the message may be read in multiple chunks (see Dealing with a Stream-based Transport in the user guide)

For UDP you're overcomplicating it. Unless you're fragmenting large messages into multiple DatagramPackets, then the DatagramPacket should contain the entire message. Decode the message directly from DatagramPacket.content(). See QuoteOfTheMomentServerHandler for an example of reading a string from a DatagramPacket.

Also see the same example for how to send a response. A new DatagramPacket is created with a message, and using the sender of the received datagram as the recipient of the new datagram. This is vital because the channel is not bound to a particular remote peer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM