下面是我的TCPServer代码
public class TCPServer {
private final int port;
public TCPServer(final int port) {
this.port = port;
}
public void run() throws Exception {
final ExecutorService threadPool = Executors.newCachedThreadPool();
final EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
final EventLoopGroup workerGroup = new NioEventLoopGroup(50, threadPool);
try {
final ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(final SocketChannel ch) throws Exception {
//ch.pipeline().addLast(new ServerRequestHandler());
ch.pipeline().addLast(new ReqMessageDecoder(1024, 0, 2, 0, 2), new ServerRequestHandler());
}
}).option(ChannelOption.SO_BACKLOG, 1000) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
final ChannelFuture f = b.bind(this.port).sync(); // (7) // Start
// the server
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to
// gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
threadPool.shutdown();
}
}
public static void main(final String[] args) throws Exception {
int port = 9090;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
}
new TCPServer(port).run();
}
}
MyRequestHandler类
public class ServerRequestHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg) { // (2)
log(" ServerRequestHandler reading message :: " + Thread.currentThread().getName());
ctx.write(msg);
}
}
我的解码器类
public class ReqMessageDecoder extends LengthFieldBasedFrameDecoder {
public ReqMessageDecoder(final int maxFrameLength, final int lengthFieldOffset, final int lengthFieldLength,
final int lengthAdjustment, final int initialBytesToStrip) {
super(maxFrameLength, lengthFieldOffset, lengthFieldLength, lengthAdjustment, initialBytesToStrip);
}
}
下面有一个示例TCP客户端代码,该代码以循环方式将示例消息循环发送到上述服务器79次。
public static void main(final String args[]) {
int i = 80;
final String message = "This is a sample message but we can send actual";
final TestClass testClass = new TestClass();
testClass.startConnection("localhost", 9090);
while (i > 1) {
i--;
testClass.sendMessageLength(testClass.getMessageLength(message).getBytes());
testClass.sendMessage(message);
}
testClass.stopConnection();
}
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
private OutputStream outputStream;
public String getMessageLength(final String message) {
final int firstByte = message.length() >> 8;
final int secondByte = message.length() - (firstByte << 8);
System.out.println("char 0 " + (char) firstByte + " char 1 " + (char) secondByte);
final String str = new String(new char[] { (char) firstByte, (char) secondByte });
System.out.println("firstByte :: " + firstByte + " secondByte :: " + secondByte + "Str :: " + str);
return str;
}
public void startConnection(final String ip, final int port) {
try {
this.clientSocket = new Socket(ip, port);
this.outputStream = this.clientSocket.getOutputStream();
this.out = new PrintWriter(this.outputStream, true);
this.in = new BufferedReader(new InputStreamReader(this.clientSocket.getInputStream()));
} catch (final UnknownHostException e) {
e.printStackTrace();
} catch (final IOException e) {
e.printStackTrace();
}
}
public String sendMessage(final String msg) {
this.out.println(msg);
String resp = null;
try {
resp = this.in.readLine();
System.out.println("resp :: " + resp);
} catch (final IOException e) {
e.printStackTrace();
}
return resp;
}
public void sendMessageLength(final byte[] msg) {
try {
this.outputStream.write(msg);
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
在我的客户端中,我首先在套接字输出流上以2个字节的形式发送消息的长度(请参见getMessageLength
),然后是实际的消息(请参见sendMessage
)。现在,当服务器收到消息时,它将在下面打印。
输出:
channel Active
ServerRequestHandler reading message :: pool-1-thread-1 message recieved :: This is a sample message but we can send actualmessage ended
exception caught
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 1024: 2562 - discarded
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:522)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.failIfNecessary(LengthFieldBasedFrameDecoder.java:500)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.exceededFrameLength(LengthFieldBasedFrameDecoder.java:387)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:430)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:343)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:441)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:278)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:656)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:591)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:508)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:470)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:909)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
channel read complete
channel inactive
在我的代码中,我没有发送大于50个字节的数据,但不确定为什么TCPserver无法使帧大小大于1024。我怀疑长度计算无法按预期进行。
我是Netty的新手,也没有其他问题。
channelRead
方法是否针对收到的每条消息在单独的线程中调用?channelRead
?channelRead
方法内的生成线程?)或NioEventLoopGroup支持开箱即用吗?答案 0 :(得分:0)
ch.pipeline().addLast(new ReqMessageDecoder(1024, 0, 2, 0, 2), new ServerRequestHandler());
您的ReqMessageDecoder期望帧的最大长度为1024,超过该长度将导致异常。您如何定义框架边界?帧边界将告诉您的解码器一个完整的帧已被接收,并且可以由您的应用程序代码处理,否则解码器将继续读取输入的数据,直到达到最大帧大小(例如1024)或帧边界字符/序列为止收到。
请参阅以下链接,了解netty内置的基于行尾的解码器,您可以将其添加到频道DelimiterBasedFrameDecoder.
答案 1 :(得分:0)
因此,经过大量的尝试和错误并通过不同的链接,我已经解决了这个问题,而我的PoC并不完整。
new ReqLengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, -2, 2)
// This will read 1st 2 bytes as length and will skip those to read the next
// bytes (i.e. length mentioned in 1st 2 bytes)
是否为每个收到的消息在单独的线程中调用channelRead方法?
回答:为此,您需要执行以下操作。
pipeline.addLast(this.threadPool, new ServerRequestHandler());
// this.threadPool refers to UnorderedThreadPoolEventExecutor
// ServerRequestHandler extends SimpleChannelInboundHandler<T>
请参见Netty 4. Parallel processing after ByteToMessageCodec
解码方法是否在阻塞线程中调用,该线程以阻塞方式从TCP / IP流中读取(n)个字节并将控件传递给requestHandler以进行channelRead?
回答:是的,但是这些线程与I / O线程不同。这些线程在通道管道上工作。
如何仅接受使用服务器端安全套接字创建的连接?
答案:请参见下面的基本设置
// some dummy SSL setup. actual production code will have some more
// sophisticated steps.
final SelfSignedCertificate ssc = new SelfSignedCertificate();
SslContext sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
pipeline.addLast(sslCtx.newHandler(ch.alloc()));
我想在并发线程中处理来自单个客户端的消息,以增加服务器的吞吐量...我是否还需要做其他任何事情(在channelRead方法内生成线程?),或者NioEventLoopGroup支持此功能?盒子?
回答:请参阅Netty multi threading per connection& Netty 4. Parallel processing after ByteToMessageCodec