下面的基于字符串的服务器在功能上等同于其数据报服务器兄弟吗?
我看到的唯一值得注意的差异,以及我尝试实现的唯一区别,是从NioDatagramChannel
转到nioServerSocketChannel
。处理程序之间存在一些细微差别,但两个处理程序都会响应" QOTM"与nextQuote()
?
为了简洁和理智,我不能包括客户。我不熟悉netty,而且我找不到关于这个主题的4.x文档。 Netty in Action确实说:
7.2.4 MessageToMessageDecoder - 即时解码POJO如果要将消息解码为另一种类型的消息MessageToMessageDecoder 是要走的路。它允许一种简单的方法。语义是 与我们之前解释的所有其他解码器完全相同。
但为了简单起见,我只是暂时尝试使用String en / de-coders。我在服务器中正确使用它们吗?
另见:
http://seeallhearall.blogspot.ca/2012/05/netty-tutorial-part-1-introduction-to.html
数据报服务器:
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();
}
}
}
字符串服务器:
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();
}
}
}
数据报服务器处理程序:
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());
}
}
数据报服务器初始化程序:
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());
}
}
字符串服务器处理程序:
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
}
}
}
字符串服务器初始化程序:
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());
}
}
答案 0 :(得分:3)
您似乎在TCP版本中正确使用字符串编码器。
然而,TCP和UDP并不相同。使用TCP连接,管道中的第一个传入处理程序将接收包含从套接字缓冲区读取的数据的缓冲区。套接字(以及通道)绑定到远程对等体上的套接字,因此通道包含与远程对等体通信所需的所有信息。这包括远程地址和端口。 Netty还可以创建一个连接到该通道的独立管道实例(取决于您如何设置它)。
尽管API类似,但UDP的行为却截然不同。它不是流,但是一组离散数据报包和单个通道将接收来自许多不同发送者的数据报包。管道中的第一个传入处理程序将接收至少包含您的有效负载(您的消息)和发送方的DatagramPacket。 DelimiterBasedFrameDecoder不能在DatagramPacket上工作 - 它需要一个ByteBuf,并且设计用于可以在多个块中读取消息的流(参见用户指南中的Dealing with a Stream-based Transport)
对于UDP,你过度复杂了。除非您将大型消息分段为多个DatagramPackets,否则DatagramPacket应包含整个消息。直接从DatagramPacket.content()解码消息。有关从DatagramPacket读取字符串的示例,请参阅QuoteOfTheMomentServerHandler。
另请参阅有关如何发送回复的相同示例。使用消息创建新的DatagramPacket,并使用接收的数据报的发送者作为新数据报的接收者。这一点至关重要,因为该频道未绑定到特定的远程对等体。