I'm new to netty. Is this an expected behaviour?
A bit more detailed:
public class Test {
public static void connect(){
EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bs = new Bootstrap();
bs.group(workerGroup);
bs.channel(NioSocketChannel.class);
bs.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000);
bs.handler( new ChannelInitializer<SocketChannel>(){
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pl = ch.pipeline();
pl.addLast("readTimeoutHandler", new ReadTimeoutHandler(1000,
TimeUnit.MILLISECONDS));
pl.addLast("framer", new DelimiterBasedFrameDecoder(
16384, Delimiters.lineDelimiter()));
pl.addLast("string-decoder", new StringDecoder());
pl.addLast("handler",
new SimpleChannelInboundHandler<String> (String.class){
#Override
protected void channelRead0(ChannelHandlerContext ctx,
String msg) throws Exception {
System.out.println(msg);
}
#Override
protected void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) throws Exception {
if(cause instanceof ReadTimeoutException){
System.out.println("Timed out.");
}
ctx.close();
}
});
}
});
bs.connect("127.0.0.1", 45001);
}
}
This is just test case, so it might be a bit incorrect, pipeline ressembles my actual pipeline close enough though.
Basicly if I change EventLoopGroup initialization from NioEventLoopGroup to OioEventLoopGroup and bootstrap channel setup from bootstrap.channel(NioSocketChannel.class) to bootstrap.channel(OioSocketChannel.class) without touching anything else, ReadTimeoutHandler stops throwing ReadTimeoutExceptions.
This was fixed in Netty 4.0.4.Final . Please upgrade, see [1].
[1] https://github.com/netty/netty/issues/1614
Related
I'm using Netty server to solve this: reading a big file line by line and processing it. Doing it on single machine is still slow, so I've decided to use server to serve chunks of data to clients. That already works, but what I also want is that server shut downs itself when processed whole file. The source code I'm using right now is:
public static void main(String[] args) {
new Thread(() -> {
//reading the big file and populating 'dq' - data queue
}).start();
final EventLoopGroup bGrp = new NioEventLoopGroup(1);
final EventLoopGroup wGrp = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
p.addLast(new StringDecoder());
p.addLast(new StringEncoder());
p.addLast(new ServerHandler(dq, bGrp, wGrp));
}
});
ChannelFuture f = b.bind(PORT).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
class ServerHandler extends ChannelInboundHandlerAdapter {
public ServerHandler(dq, bGrp, wGrp) {
//assigning params to instance fields
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
//creating bulk of data from 'dq' and sending to client
/* e.g.
ctx.write(dq.get());
*/
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
if (dq.isEmpty() /*or other check that file was processed*/ ) {
try {
ctx.channel().closeFuture().sync();
} catch (InterruptedException ie) {
//...
}
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
ctx.close();
ctx.executor().parent().shutdownGracefully();
}
}
Is the server shutdown in channelReadComplete(...) method correct? What I'm afraid is that there can still be another served client (e.g. sending big bulk in other client and with current client reached the end of 'dq').
The base code is from netty EchoServer/DiscardServer examples.
The question is : how to shut down netty server (from handler) when reached specific condition.
Thanks
You can not shutdown a server from a handler. What you could do is signal to a different thread that it should shutdown the server.
on some day i decided to create a Netty Chat server using Tcp protocol. Currently, it successfully logging connect and disconnect, but channelRead0 in my handler is never fires. I tried Python client.
Netty version: 4.1.6.Final
Handler code:
public class ServerWrapperHandler extends SimpleChannelInboundHandler<String> {
private final TcpServer server;
public ServerWrapperHandler(TcpServer server){
this.server = server;
}
#Override
public void handlerAdded(ChannelHandlerContext ctx) {
System.out.println("Client connected.");
server.addClient(ctx);
}
#Override
public void handlerRemoved(ChannelHandlerContext ctx) {
System.out.println("Client disconnected.");
server.removeClient(ctx);
}
#Override
public void channelRead0(ChannelHandlerContext ctx, String msg) {
System.out.println("Message received.");
server.handleMessage(ctx, msg);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("Read complete.");
super.channelReadComplete(ctx);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
Output:
[TCPServ] Starting on 0.0.0.0:1052
Client connected.
Read complete.
Read complete.
Client disconnected.
Client code:
import socket
conn = socket.socket()
conn.connect(("127.0.0.1", 1052))
conn.send("Hello")
tmp = conn.recv(1024)
while tmp:
data += tmp
tmp = conn.recv(1024)
print(data.decode("utf-8"))
conn.close()
Btw, the problem was in my initializer: i added DelimiterBasedFrameDecoder to my pipeline, and this decoder is stopping the thread. I dont know why, but i dont needed this decoder, so i just deleted it, and everything started to work.
#Override
protected void initChannel(SocketChannel ch) throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = ch.pipeline();
// Protocol Decoder - translates binary data (e.g. ByteBuf) into a Java object.
// Protocol Encoder - translates a Java object into binary data.
// Add the text line codec combination first,
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); //<--- DELETE THIS
pipeline.addLast("decoder", new StringDecoder());
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new ServerWrapperHandler(tcpServer));
}
I'v got no idea about the "read" method of the ChannelOutboundHandle.
why this method occurred in the ChannelOutboundHandle, what's its for?
cause the outboundHandlers are used to handle the output, they are usually
used to write data to wire.
and also I got a problem when I'm using the "read" method.I wrote a EchoServer by netty. here is my code:
public class EchoServer {
public static void main(String[] args) {
// this handler is sharable
final EchoServerInboundHandler echoServerInboundHandler = new EchoServerInboundHandler();
EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
// setup group
serverBootstrap.group(eventLoopGroup)
// setup channelFactory
.channel(NioServerSocketChannel.class)
// listening port
.localAddress(new InetSocketAddress(8080))
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(echoServerInboundHandler);
pipeline.addLast(new EchoServerOutboundHandler());
}
});
try {
ChannelFuture f = serverBootstrap.bind().sync();
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println(future.channel().localAddress()+" started!");
}
}
});
f.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// release all threads
eventLoopGroup.shutdownGracefully().syncUninterruptibly();
}
}
}
public class EchoServerOutboundHandler extends ChannelOutboundHandlerAdapter {
#Override
public void read(ChannelHandlerContext ctx) throws Exception {
System.out.println("read");
//super.read(ctx);
}
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
System.out.println("msg:"+msg);
super.write(ctx, msg, promise);
}
#Override
public void flush(ChannelHandlerContext ctx) throws Exception {
super.flush(ctx);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
#ChannelHandler.Sharable
public class EchoServerInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ctx.writeAndFlush(msg);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("client:"+ctx.channel().remoteAddress()+" connected!");
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("client:"+ctx.channel().remoteAddress()+" disconnected!");
}
#Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("EchoServerInboundHandler registered");
super.channelRegistered(ctx);
}
#Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("EchoServerInboundHandler unregistered");
super.channelUnregistered(ctx);
}
}
as U can see, I comment the super.read(ctx); in the read method of the EchoServerOutboundHandler which cause a problem that it can't write data to the client. and also I found that this method will be called when a client established a connection.
At the very least, you need to provide the same code that the parent object would do in the super() method, which is ctx.read()
Which is what the JavaDoc says...
Calls ChannelHandlerContext.read() ...
Otherwise, all you're doing is printing something on your own, and not using Netty to handle anything.
This question already has answers here:
Official reasons for "Software caused connection abort: socket write error"
(14 answers)
Closed 5 years ago.
I'm trying to go through the SSL example and EchoServer example in Netty and for some reason, when I add my sslContext on the client side, I keep getting, an established connection was aborted by the software in your host machine.
EchoServerBootstrap
public class EchoServerBootstrap {
private final int port;
public EchoServerBootstrap(int port) {
this.port = port;
}
public static void main(String[] args) throws Exception {
new EchoServerBootstrap(3000).start();
}
public void start() throws Exception {
SelfSignedCertificate ssc = new SelfSignedCertificate();
final SslContext sslContext = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group)
.channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(sslContext.newHandler(ch.alloc()));
ch.pipeline().addLast(new EchoServerHandler());
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
}
EchoServerHandler
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf in = (ByteBuf) msg;
System.out.println("Received: " + in.toString(CharsetUtil.UTF_8));
ctx.write(in);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
System.out.println("channel read complete");
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
EchoClientHandler
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
#Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks", CharsetUtil.UTF_8));
}
#Override
public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
System.out.println("Client receive: " + in.toString(CharsetUtil.UTF_8));
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
EchoClientBootstrap
public class EchoClientBootstrap {
private final String host;
private final int port;
public EchoClientBootstrap(String host, int port) {
this.port = port;
this.host = host;
}
public void start() throws Exception {
EventLoopGroup group = new NioEventLoopGroup();
final SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(sslContext.newHandler(ch.alloc(), host, port)); // WHEN I ADD THIS LINE IT FAILS WITH I/O EXCEPTION 'an established connection was aborted...'
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect(host, port).sync();
f.channel().closeFuture().sync();
} finally {
group.shutdownGracefully().sync();
}
}
public static void main(String[] args) throws Exception {
new EchoClientBootstrap("localhost", 3000).start();
}
}
Is there something obvious I'm missing? I tried following this example and altering it a bit (http://netty.io/4.1/xref/io/netty/example/securechat/package-summary.html), but I keep getting that exception when I add the sslContext to the client channel. Any thoughts?
I added some logging to figure out what was going on, and without encryption, EchoClient sends over the "Netty rocks" message and the server reads the message and closes the channel. But for some reason if SSL is enabled, the EchoServerHandler calls channelReadComplete before the EchoClient can send "Netty rocks" which is essentially this method
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
System.out.println("channel read complete");
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
which was closing my channel. I am not sure why there is that discrepancy when using SSL.
I am going through netty's documentation here and the diagram here.
My question is, the Timeserver is writing time into the socket, for the client to read the time. Shouldn't it use the ChannelOutboundHandlerAdapter ? Why is the logic in ChannelInboundHandlerAdapter ?
Couldn't understand, please explain.
Timeserver,
public class TimeServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelActive(final ChannelHandlerContext ctx) { // (1)
final ByteBuf time = ctx.alloc().buffer(4); // (2)
time.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(time); // (3)
f.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
assert f == future;
ctx.close();
}
}); // (4)
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
TimeClient,
public class TimeClient {
public static void main(String[] args) throws Exception {
String host = args[0];
int port = Integer.parseInt(args[1]);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); // (1)
b.group(workerGroup); // (2)
b.channel(NioSocketChannel.class); // (3)
b.option(ChannelOption.SO_KEEPALIVE, true); // (4)
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new TimeClientHandler());
}
});
// Start the client.
ChannelFuture f = b.connect(host, port).sync(); // (5)
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
}
}
The reason we use a ChannelInboundHandlerAdapter is because, we are writing into the channel which was established by the client to the server. Since its inbound with respect to the server, we use a ChannelInboundHandlerAdapter. The Client connects to the Server, through the channel into which the server sends out the time.
Because your server will respond to incoming messages, it will need to implement interface ChannelInboundHandler, which defines methods for acting on inbound events.
Further, ChannelInboundHandlerAdapter has a straightforward API, and each of its methods can be overridden to hook into the event lifecycle at the appropriate point.
I just started learning Netty, hope this helps. :)