I just can't realize why my read time out is not working. All I want to do is just to wait
for 10 seconds for some thread to put message to BlockedQueue<String> and on timeout return some kind of response on client.
public class NioAsynChatPipelineFactory implements ChannelPipelineFactory {
private static Timer timer = new HashedWheelTimer();
private final ChannelHandler timeoutHandler = new ReadTimeoutHandler(timer, 10);
#Override
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", new HttpRequestDecoder());
pipeline.addLast("encoder", new HttpResponseEncoder());
pipeline.addLast("handler", new NioAsynChatHandler());
pipeline.addLast("timeout", this.timeoutHandler);
return pipeline;
}
}
Now my handler looks like this.
public class NioAsynChatHandler extends SimpleChannelUpstreamHandler{
#Override
public void handleUpstream(
ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
super.handleUpstream(ctx, e);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
System.out.println("Exception");
\\writing some kind of response and closing channel.
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
Thread thread = new Thread(new ConsmerTask(e.getChannel()));
thread.start();
}
and inside ConsumerTask I'm just waiting for BlockingQueue to get response
public class ConsumerTask implements Runnable{
private Channel channel;
public ConsumerTask(Channel channel){
this.channel = channel;
}
#Override
public void run() {
try{
while(true){
String message = queue.take();
}
} catch(InterruptedException ex){
Thread.currentThread.interrupt();
} finally{
//write something to channel and close it
}
}
My problem is that I don't see that any excpetion occurs on time out.
What am I doing wrong?
Update:
public static final BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<String>();
Actually my question is more generic, How to close channel on timeout while it is waiting for something in external thread?
Update 2:
Another question: due to the fact that I'm running external thread in Cha would it be better to use OrderedMemoryAwareThreadPoolExecutor in pipeline? Will it increase performance.
It's basically because you put the ReadTimeoutHandler in the wrong position. Please put it in the first position of the pipeline (i.e. before all handlers).
Related
I am writing a minecraft server in java from scratch for private reasons.
I am new to the netty api so please explain how I can fix it
My problem is pretty simple my server waits for a connection then reads data from that connection but it never reads the then bit of info
https://wiki.vg/Server_List_Ping
I followed that and everything goes well up until the request packet which my server never reads it?
I don't know what the problem is I think it's because its closing the connection but I have no idea how to stop that
Here's the code
public class DataHandler extends SimpleChannelInboundHandler {
public void initChannel(NioServerSocketChannel nioServerSocketChannel) throws Exception {
try {
System.out.println("Data Handler");
}catch (Exception e) {
}
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println("[DEBUG] Read complete");
//ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
// .addListener(ChannelFutureListener.CLOSE);
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("Data Handler active");
ctx.channel().read();
//ctx.pipeline().addLast("encoder",new Encoder());
//ctx.fireChannelActive();
}
private int pos = 0;
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buf = (ByteBuf) msg;
//ByteBuf packet = buf.readBytes(length);
int length = readVarInt(buf);
int ID = readVarInt(buf);
System.out.println("[DEBUG] PACKET ID: "+ID);
Packet packet = PacketUtil.createPacket(ID,length,buf,ctx);
packet.readBuf();
Object ran = null;
//super.channelRead(ctx, msg);
}
#Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
System.out.println("Test");
}
}
There is some trial and error comments in there I did not know if I should have left them in
Heres the main class
public class Server {
private int port;
public void run() throws IOException {
port = 25565;
EventLoopGroup mainGroup = new NioEventLoopGroup();
EventLoopGroup threadGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(mainGroup, threadGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new DataHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 5)
.option(ChannelOption.AUTO_READ,true)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture channelFuture = b.localAddress(port).bind().sync();
System.out.println(String.format("Started on port %d", port));
System.out.println("Registering packets");
PacketUtil.registerPackets();
}catch(InterruptedException e) {
}
}
}
Initially able to make the connection. Simply close the connection client and try to connect again or restart the client. the connection is not established. It creates connection only once.
Can someone help me to improve it. So, it can handle n number client simultaneously.
bossGroup = new NioEventLoopGroup(1);
workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(new DelimiterBasedFrameDecoder(20000, Delimiters.lineDelimiter()));
// p.addLast(new StringDecoder());
// p.addLast(new StringEncoder());
p.addLast(serverHandler);
}
});
// Start the server.
LOGGER.key("Simulator is opening listen port").low().end();
ChannelFuture f = b.bind(config.getPort()).sync();
LOGGER.key("Simulator started listening at port: " + config.getPort()).low().end();
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
} finally {
// Shut down all event loops to terminate all threads.
LOGGER.key("Shtting down all the thread if anyone is still open.").low().end();
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
Server Handler code is below:
public class SimulatorServerHandler extends SimpleChannelInboundHandler<String> {
private AtomicReference<ChannelHandlerContext> ctxRef = new AtomicReference<ChannelHandlerContext>();
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private AtomicInteger seqNum = new AtomicInteger(1);
private final Configuration configure;
private ScheduledFuture<?> hbTimerWorker;
private final int stx = 0x02;
private final int etx = 0x03;
private final ILogger LOGGER;
public int enablePublishFunction = 0;
public SimulatorServerHandler(Configuration config) {
this.configure = config;
//LOGGER = LogFactory.INSTANCE.createLogger();
LOGGER = new LogFactory().createLogger("SIM SERVER");
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctxRef.set(ctx);
enablePublishFunction =1;
// System.out.println("Connected!");
LOGGER.low().key("Gateway connected to the Simulator ").end();
startHBTimer();
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelInactive();
hbTimerWorker.cancel(false);
enablePublishFunction =0;
LOGGER.low().key("Gateway disconnected from the Simulator ").end();
}
#Override
public void channelRead0(ChannelHandlerContext ctx, String request) {
// Generate and write a response.
String response;
boolean close = false;
/* if (request.isEmpty()) {
response = "Please type something.\r\n";
} else if ("bye".equals(request.toLowerCase())) {
response = "Have a good day!\r\n";
close = true;
} else {
response = "Did you say '" + request + "'?\r\n";
}
// We do not need to write a ChannelBuffer here.
// We know the encoder inserted at TelnetPipelineFactory will do the conversion.
ChannelFuture future = ctx.write(response);
// Close the connection after sending 'Have a good day!'
// if the client has sent 'bye'.
if (close) {
future.addListener(ChannelFutureListener.CLOSE);
}
*/
System.out.println(request);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
LOGGER.key("Unknown exception while network communication :"+ cause.getStackTrace()).high().end();
cause.printStackTrace();
ctx.close();
}
Maybe because you use always the very same server handler in your pipeline for all connections (not using new ServerHandler())? Side effects in your implementation could block your handler to be reusable.
I'm having a difficult time getting my head around how to use ThreadRenamingRunnable to rename the worker thread in netty. I am new to netty and using netty 3.9.0-Final.
I want to rename the worker threads..."New I/O worker #X". I'm ok with the name of the boss thread.
This is a basic server which responds to a "ping" with a "pong".
public class NettyPingPong {
public static void main(String[] args) {
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new LineBasedFrameDecoder(255,true,true),
new PongUpstreamHandler(),
new StringEncoder());
}
});
bootstrap.bind(new InetSocketAddress(8899));
out.println("im ready");
}
}
and
public class PongUpstreamHandler extends SimpleChannelUpstreamHandler {
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
String message = new String(buffer.array());
if (message.equalsIgnoreCase("ping")){
e.getChannel().write("pong\n");
out.println("ponged...");
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
e.getChannel().close();
out.println("closed...");
}
}
1.5 years old question :( I landed here with the same question, working on Netty 3.5.0 Final. And I resolved with the below code.
I used a thread factory which would also generage meaningful threadNames similar to what Abe has mentioned.
And, I configured ThreadRenamingRunnable not to rename the threads I supply. Using ThreadFactory and setting ThreadNameDeterminer as CURRENT.
ThreadRenamingRunnable.setThreadNameDeterminer(ThreadNameDeterminer.CURRENT);
Netty (3.5.0 atleast) alters the threadName from its original value to a proposed value "New I/O Worker #X". The above code snippet, ensures that it doesn't alter the thread name. The name is determined by the below ThreadFactory.
public class CustomThreadFactory implements ThreadFactory {
private final AtomicInteger threadIdSequence = new AtomicInteger(0);
private String threadNamePrefix = "Netty-Worker-";
public CustomThreadFactory() {
}
public CustomThreadFactory(String namePrefix) {
this.threadNamePrefix = namePrefix;
}
#Override
public Thread newThread(Runnable runnable) {
Thread newThread = new Thread(runnable, threadNamePrefix + threadIdSequence.incrementAndGet());
if (newThread.isDaemon()) {
newThread.setDaemon(false);
}
if (newThread.getPriority() != Thread.NORM_PRIORITY) {
newThread.setPriority(Thread.NORM_PRIORITY);
}
newThread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(final Thread thread, final Throwable e) {
System.err.println(thread + " threw exception: " + e.getMessage());
e.printStackTrace();
}
});
return newThread;
}
}
There may be better ways to do this, but this is how i got it to work. Thanks to this blog.
public class NettyPingPong {
public static void main(String[] args) {
final String WORKER_THREADNAME_PREFIX = "worker";
NioWorkerPool workerPool = new NioWorkerPool(Executors.newCachedThreadPool(), 20, new ThreadNameDeterminer() {
#Override
public String determineThreadName(String currentThreadName,String proposedThreadName) throws Exception {
StringBuilder sb = new StringBuilder(WORKER_THREADNAME_PREFIX);
sb.append(currentThreadName.substring(currentThreadName.lastIndexOf('-')));
return sb.toString();
}
});
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
workerPool));
bootstrap.setPipelineFactory(
new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(
new LineBasedFrameDecoder(255, true, true),
new PongUpstreamHandler(),
new StringEncoder());
}
});
bootstrap.bind(new InetSocketAddress(8899));
out.println("im ready");
}
}
You can pass in a ThreadFactory which will name your thread. Take a look at this ThreadFactory I am using to name the server threads. Provided below is the example usage
serverBootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(Executors
.newCachedThreadPool(new NamedThreadFactory(
"TCP-Server-Boss")), Executors
.newCachedThreadPool(new NamedThreadFactory(
"TCP-Server-Worker"))));
I'm getting a java.nio.channels.NotYetConnectedException in the following code because I'm trying to write to a channel that is not yet open.
Essentially what I have is a channel pool in which I grab a channel to write to if one is free, and I create a new channel if one is not available. My problem is that when I create a new channel, the channel is not ready for writing when I call connect, and I don't want to wait for the connection to open before returning because I don't want to block the thread. What's the best way to do this? Also, is my logic for retrieving/returning channels valid? See code below.
I have a simple connection pool like the following:
private static class ChannelPool {
private final ClientBootstrap cb;
private Set<Channel> activeChannels = new HashSet<Channel>();
private Deque<Channel> freeChannels = new ArrayDeque<Channel>();
public ChannelPool() {
ChannelFactory clientFactory =
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
cb = new ClientBootstrap(clientFactory);
cb.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(
new HttpRequestEncoder(),
new HttpResponseDecoder(),
new ResponseHandler());
}
});
}
private Channel newChannel() {
ChannelFuture cf;
synchronized (cb) {
cf = cb.connect(new InetSocketAddress("localhost", 18080));
}
final Channel ret = cf.getChannel();
ret.getCloseFuture().addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture arg0) throws Exception {
System.out.println("channel closed?");
synchronized (activeChannels) {
activeChannels.remove(ret);
}
}
});
synchronized (activeChannels) {
activeChannels.add(ret);
}
System.out.println("returning new channel");
return ret;
}
public Channel getFreeChannel() {
synchronized (freeChannels) {
while (!freeChannels.isEmpty()) {
Channel ch = freeChannels.pollFirst();
if (ch.isOpen()) {
return ch;
}
}
}
return newChannel();
}
public void returnChannel(Channel ch) {
synchronized (freeChannels) {
freeChannels.addLast(ch);
}
}
}
I'm trying to use this inside a handler as follows:
private static class RequestHandler extends SimpleChannelHandler {
#Override
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) {
final HttpRequest request = (HttpRequest) e.getMessage();
Channel proxyChannel = pool.getFreeChannel();
proxyToClient.put(proxyChannel, e.getChannel());
proxyChannel.write(request);
}
}
Instead of adding the new channel to activeChannels immediately after bootstrap.connect(..), you have to add a listener to the ChannelFuture which was returned by bootstrap.connect(..), and add the channel to activeChannels in the added listener. That way, getFreeChannel() will never get the channel that is not connected yet.
Because it is likely that activeChannels is empty even if you called newChannel() (newChannel() will return even before connection is established), you have to decide what to do in such a case. If I were you, I would change the return type of getFreeChannel() from Channel to ChannelFuture so that the caller gets notified when the free channel is ready.
This is my first time using Netty and I'm having trouble making a simple echo server! I looked at docs and it says to use the string encoder and decoder, which I am not using properly apparently. For the framedecoder, I'd like to use the header messages with one byte length, but that doesn't seem to be working either due to the string issue. I assume my implementation of the PipelineFactory is messed up.
Bonus Question:
Because I'm stupid and ambitious, I tried implementing a timeout/heartbeat handler. That didn't work either.
Here are the console output and java code:
Console:
>>telnet localhost 6969
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
>>3
Connection closed by foreign host.
Java Console:
Starting server on 6969
channelConnected
channelDisconnected
java.lang.IllegalArgumentException: unsupported message type: class java.lang.String
at org.jboss.netty.channel.socket.nio.SocketSendBufferPool.acquire(SocketSendBufferPool.java:51)
at org.jboss.netty.channel.socket.nio.NioWorker.write0(NioWorker.java:455)
...
Server.java
public class Server {
public static void main(String[] args) throws Exception {
ChannelFactory factory =
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool());
ServerBootstrap bootstrap = new ServerBootstrap(factory);
Timer timer = new HashedWheelTimer();
bootstrap.setPipelineFactory(new MyPipelineFactory(timer) {
public ChannelPipeline getPipeline() {
return Channels.pipeline(new ServerHandler());
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
bootstrap.bind(new InetSocketAddress(6969));
System.out.println("Starting server on 6969");
}
}
ServerHandler.java
public class ServerHandler extends SimpleChannelHandler {
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e){
Channel ch = e.getChannel();
System.out.println("channelConnected");
}
#Override
public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e){
Channel ch = e.getChannel();
System.out.println("channelDisconnected");
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
String msg = (String) e.getMessage();
e.getChannel().write("Did you say '" + msg + "'?\n");
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
MyPipelineFactory.java
public class MyPipelineFactory implements ChannelPipelineFactory {
private final Timer timer;
private static ChannelHandler idleStateHandler;
public MyPipelineFactory(Timer t) {
this.timer = t;
//this.idleStateHandler = new IdleStateHandler(timer, 5, 20, 0); // timer must be shared
}
public ChannelPipeline getPipeline() {
// create default pipeline from static method
ChannelPipeline pipeline = Channels.pipeline();
// Decoders
int maxFrameLength = 1024;
pipeline.addLast("framer", new DelimiterBasedFrameDecoder(maxFrameLength, Delimiters.lineDelimiter()));
//pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength,0,1)); // get header from message
pipeline.addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
// Encoders
pipeline.addLast("stringEncoder", new StringEncoder(CharsetUtil.UTF_8));
// Idle state handling- heartbeat
//pipeline.addLast("idleStateHandler", idleStateHandler);
return pipeline;
}
}
Bonus, because I'm stupid and want to get in over my head...
HeartbeatHandler.java
public class HeartbeatHandler extends IdleStateAwareChannelHandler {
#Override
public void channelIdle(ChannelHandlerContext ctx, IdleStateEvent e) {
if (e.getState() == IdleState.READER_IDLE) {
System.out.println("Reader idle, closing channel");
e.getChannel().close();
}
else if (e.getState() == IdleState.WRITER_IDLE) {
System.out.println("Writer idle, sending heartbeat");
e.getChannel().write("heartbeat"); //
}
}
}
It's because you mess up the ChannelPipeline.
You use:
bootstrap.setPipelineFactory(new MyPipelineFactory(timer) {
public ChannelPipeline getPipeline() {
return Channels.pipeline(new ServerHandler());
}
});
What you would need todo is modify the MyPipelineFactory class and add your ServerHandler in there. Then just set it like:
bootstrap.setPipelineFactory(new MyPipelineFactory(timer));
Then everything should work. Even your timeout stuff ;)