Unfortunately, I don't understand this output from the netty server:
BUILD SUCCESSFUL
Total time: 3 seconds
Jul 27, 2014 2:04:44 AM io.netty.handler.logging.LoggingHandler channelRegistered
INFO: [id: 0xcad25a31] REGISTERED
Jul 27, 2014 2:04:44 AM io.netty.handler.logging.LoggingHandler bind
INFO: [id: 0xcad25a31] BIND(0.0.0.0/0.0.0.0:4454)
Jul 27, 2014 2:04:44 AM io.netty.handler.logging.LoggingHandler channelActive
INFO: [id: 0xcad25a31, /0:0:0:0:0:0:0:0:4454] ACTIVE
Jul 27, 2014 2:04:59 AM io.netty.handler.logging.LoggingHandler logMessage
INFO: [id: 0xcad25a31, /0:0:0:0:0:0:0:0:4454] RECEIVED: [id: 0xff40b8a2, /127.0.0.1:37558 => /127.0.0.1:4454]
Jul 27, 2014 2:04:59 AM net.bounceme.dur.netty.ServerHandler <init>
INFO: starting..
Jul 27, 2014 2:04:59 AM io.netty.channel.DefaultChannelPipeline$TailContext exceptionCaught
WARNING: An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 1048576: 2901213193 - discarded
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:501)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.failIfNecessary(LengthFieldBasedFrameDecoder.java:477)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:403)
at io.netty.handler.codec.serialization.ObjectDecoder.decode(ObjectDecoder.java:68)
at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:343)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:241)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:149)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:333)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:319)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:787)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:125)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:744)
^Cthufir#dur:~/NetBeansProjects/AgentServer$
thufir#dur:~/NetBeansProjects/AgentServer$
Presumably the netty-based server is complaining that it's receiving bad data in some respect?
client code:
package net.bounceme.dur.client.gui;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import net.bounceme.dur.client.jdbc.Title;
public final class ApplicationDriver {
private static final Logger log = Logger.getLogger(ApplicationDriver.class.getName());
private TitlesGUI gui = null;
private Handler handler = null;
public ApplicationDriver() throws IOException, ClassNotFoundException {
handler = new FileHandler("application.log");
handler.setFormatter(new SimpleFormatter());
log.setLevel(Level.INFO);
log.addHandler(handler);
log.info("starting log..");
MyProps p = new MyProps();
String host = p.getHost();
int port = p.getServerPort();
guiThread();
readWrite(host, port);
}
private void guiThread() {
Thread g;
g = new Thread() {
#Override
public void run() {
try {
gui = new TitlesGUI();
} catch (IOException ex) {
log.severe(ex.toString());
}
gui.setVisible(true);
}
};
g.start();
}
public static void main(String... args) throws IOException, ClassNotFoundException {
new ApplicationDriver();
}
private void readWrite(final String host, final int port) throws IOException {
Thread inputOutput;
final Socket socket = new Socket(host, port);
inputOutput = new Thread() {
#Override
public void run() {
while (true) {
try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream())) {
gui.setTitle((Title) objectInputStream.readObject());
Thread.sleep(1000);
} catch (IOException | ClassNotFoundException | InterruptedException ex) {
log.severe(ex.toString());
}
}
}
};
inputOutput.start();
}
}
is it a problem that the client is using regular sockets instead of netty? Both on the client and server side POJO's are being sent. (The Title class is serializable and the serialVersionUID values match up.)
a method from the GUI client (which is a bit large, it's a Netbeans Swing JFrame):
public void setTitle(Title title) {
this.title = title;
text.setText(title.toString());
}
the point of the above method is for something to send objects to the GUI, which is then updated accordingly. Similarly, I want to fire updates, or other-wise wire the GUI to socket i/o.
I don't really understand the output from the netty server. Is it a problem that the server uses netty while the client uses sockets? Both use the same POJO, with the serialVersionUID value. Here's the netty handler code:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.logging.Logger;
import net.bounceme.dur.jdbc.Title;
public class ServerHandler extends SimpleChannelInboundHandler<Title> {
private static final Logger log = Logger.getLogger(ServerHandler.class.getName());
public ServerHandler() {
log.info("starting..");
}
#Override
public boolean acceptInboundMessage(Object msg) throws Exception {
log.info(msg.toString());
return true;
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info(msg.toString());
ctx.write(new Title());
}
#Override
protected void channelRead0(ChannelHandlerContext chc, Title title) throws Exception {
log.info(title.toString());
chc.write(new Title());
}
}
Apparently, none of the server handler code is executed, as everything explodes immediately after the client connects.
server code:
package net.bounceme.dur.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.security.cert.CertificateException;
import java.util.logging.Logger;
import javax.net.ssl.SSLException;
public final class Server {
private static final Logger log = Logger.getLogger(Server.class.getName());
public static void main(String[] args) throws Exception {
MyProps p = new MyProps();
int port = p.getServerPort();
new Server().startServer(port, false);
}
private void startServer(int port, boolean ssl) throws CertificateException, SSLException, InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ServerHandler());
}
});
b.bind(port).sync().channel().closeFuture().sync();
log.info("connected!");
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
The TooLongFrameException raised by LengthFieldBasedFrameDecoder means one of the following:
The remote peer sent a very large message, which exceeds the limit. The default maximum length of a message is 1 MiB. If you expect to receive a message larger than that, specify an alternative maximum length when you construct a LengthFieldBasedFrameDecoder.
You passed wrong parameters to LengthFieldBasedFrameDecoder so that it is decoding a wrong place in your message. In this case, you'd better re-read the Javadoc of LengthFieldBasedFrameDecoder to specify the correct values for you.
Related
I am using a Java code in Eclipse which is supposed to read JMS messages continuously from JMS queue on Jboss EAP 6.4. However, I am getting exception when I am running this program.
I tried to do some troubleshooting but I am stuck now
The code I am using is as below (Actual IP of Jboss is replaced with #. "remote://#.#.#.#:4447")
import java.util.Hashtable;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import EDU.oswego.cs.dl.util.concurrent.CountDown;
public class GetMessageTriggerResponses
{
static CountDown done = new CountDown(10);
QueueConnection conn;
QueueSession session;
Queue recvq;
public static class ExListener implements MessageListener
{
public void onMessage(Message msg)
{
done.release();
TextMessage tm = (TextMessage) msg;
try {
System.out.println("Received message: \n" + tm.getText());
} catch (Throwable t) {
t.printStackTrace();
}
}
}
public void setup(String[] args) throws JMSException, NamingException, InterruptedException
{
Hashtable env = new Hashtable();
env.put(InitialContext.INITIAL_CONTEXT_FACTORY,"org.jboss.naming.remote.client.InitialContextFactory");
env.put(InitialContext.PROVIDER_URL, "remote://#.#.#.#:4447"); // DEV-ENV
Context context = new InitialContext(env);
QueueConnectionFactory connectionFactory = (QueueConnectionFactory) context
.lookup("jms/RemoteConnectionFactory");
Connection connection = connectionFactory.createConnection();
recvq = (Queue) context.lookup(args[0]);
System.out.println("listening on "+args[0]);
session = conn.createQueueSession(false, QueueSession.AUTO_ACKNOWLEDGE);
conn.start();
System.out.println("Begin recv message");
QueueReceiver receiver = session.createReceiver(recvq);
receiver.setMessageListener(new ExListener());
done.acquire();
}
public void stop()
throws JMSException
{
conn.stop();
session.close();
conn.close();
}
public static void main(String args[])
throws Exception
{
/*if (args.length < 1) {
System.err.println("Usage: java prog_name msg-trigger-qname");
return;
}*/
System.out.println("Begin GetMessageTriggerResponses");
GetMessageTriggerResponses client = new GetMessageTriggerResponses();
client.setup(args);
client.stop();
System.out.println("End GetMessageTriggerResponses");
System.exit(0);
}
}
I am getting the exception:
Begin GetMessageTriggerResponses
Feb 04, 2019 12:49:42 AM org.xnio.Xnio <clinit> INFO: XNIO Version 3.0.17.GA-redhat-1 Feb 04, 2019 12:49:42 AM org.xnio.nio.NioXnio <clinit> INFO: XNIO NIO Implementation Version 3.0.17.GA-redhat-1
Feb 04, 2019 12:49:42 AM org.jboss.remoting3.EndpointImpl <clinit> INFO: JBoss Remoting version 3.3.12.Final-redhat-2
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
at GetMessageTriggerResponses.setup(GetMessageTriggerResponses.java:56)
at GetMessageTriggerResponses.main(GetMessageTriggerResponses.java:88)
The problem is that you're passing in an empty array into the setup() method of GetMessageTriggerResponses and then attempting to use a value from that array. This is the problematic line:
recvq = (Queue) context.lookup(args[0]);
Either when you invoke setup() or access the array you should check to make sure it has the expected number of values. This is basic input validation. You actually have some code to validate the input in main(), but you've commented it out for some reason. I recommend you restore that code to avoid this ArrayIndexOutOfBoundsException.
**chatclient.java**
package com.examplechat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.nio.NioSctpChannel;
public class ChatClient {
public static void main(String[] args) throws InterruptedException, IOException {
new ChatClient("localhost", 8000).run();
}
private final String host;
private final int port;
public ChatClient(String host, int port) {
super();
this.host = host;
this.port = port;
}
public void run() throws InterruptedException, IOException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(group)
.channel(NioSctpChannel.class)
.handler(new ChatClientInitilizer());
Channel channel = bootstrap.connect(host, port).sync().channel();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while(true) {
channel.write(in.readLine()+ "\r\n");
}
}
finally {
group.shutdownGracefully(); }
}
}
**ChatServer.java**
package com.examplechat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
public class ChatServer {
public static void main(String[] args) throws InterruptedException {
new ChatServer(8000).run();
}
private final int port;
public ChatServer(int port) {
super();
this.port = port;
}
public void run() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChatServerInitializer());
bootstrap.bind(port).sync().channel().closeFuture().sync();
}
finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}}
I'm trying to create a chat Application using netty. When I try to run the chat client once I initiate chatserver java application. It shows
Exception in thread "main" io.netty.channel.ChannelException: Unable
to create Channel from class class
io.netty.channel.sctp.nio.NioSctpChannel at
io.netty.channel.ReflectiveChannelFactory.newChannel
(ReflectiveChannelFactory.java:40)
I've even changed the dependencies at pom.xml still i'm receiving the same error every time I run it
Update
Error-1: Unable to create Channel error has been cleared by myself
I've answered it myself please help me fix this second issue
Error-2: I'm getting a new error called WARNING: Failed to initialize a channel. Closing: [id: 0x9e4e05e0] java.lang.ClassCastException: io.netty.channel.sctp.nio.NioSctpChannel cannot be cast to io.netty.channel.socket.SocketChannel
Here's the github link if you need https://github.com/cyborgsri/Chat-Application/tree/master/netty-chat-youtube/src/main/java/com/examplechat.
I was getting error as
Unable to create Channel from class class io.netty.channel which
is Caused by: java.lang.UnsupportedOperationException:
ibsctp.so.1: cannot open shared object file: No such file or
directory`
This error was cleared once I install lksctp-tools by:
sudo apt-get install lksctp-tools
Now channel has been registered. But I'm getting a new error that
WARNING:
Failed to initialize a channel. Closing: [id: 0x9e4e05e0]
java.lang.ClassCastException: io.netty.channel.sctp.nio.NioSctpChannel
cannot be cast to io.netty.channel.socket.SocketChannel
The error happens because you use:
public class ChatServerInitializer extends ChannelInitializer<SocketChannel>
Change it to:
public class ChatServerInitializer extends ChannelInitializer<Channel>
For right now, the client instantiates the Swing GUI. This seems to work, to a degree.
What's the correct flow to allow messages to get passed to, and received from, the GUI?
In the GUI, which extends JFrame for convenience:
private void nextTitleActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextTitleActionPerformed
title = new Title();
}//GEN-LAST:event_nextTitleActionPerformed
how do I pass that title through netty, that is, to the ClientHandler, so that this object is sent from the client to the server?
I toyed with starting the Client and ClientHandler from within the GUI directly, but there's no real "instance" of ClientHandler to invoke method calls on.
Perhaps there's a Netty helper or interface which the Swing GUI can pass methods through? I've read a bit of a book on netty, but that book doesn't seem to address this topic. (Hmm, I'll take another look at the appendix, it has some demo's.)
client code:
package net.bounceme.dur.client.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import java.util.logging.Logger;
import javax.net.ssl.SSLException;
import net.bounceme.dur.client.gui.TitlesGUI;
public final class Client {
private static final Logger log = Logger.getLogger(Client.class.getName());
private final TitlesGUI gui = new TitlesGUI();
public Client() {
}
public static void main(String... args) throws InterruptedException, SSLException {
MyProps p = new MyProps();
String host = p.getHost();
int port = p.getServerPort();
new Client().startClient(host, port);
}
public void startClient(final String host, final int port) throws SSLException, InterruptedException {
gui.setVisible(true);
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline p = ch.pipeline();
p.addLast(
new ObjectEncoder(),
new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
new ClientHandler());
}
});
b.connect(host, port).sync().channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
handler code:
package net.bounceme.dur.client.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.logging.Logger;
import net.bounceme.dur.client.jdbc.Title;
public class ClientHandler extends SimpleChannelInboundHandler<Title> {
private static final Logger log = Logger.getLogger(ClientHandler.class.getName());
public ClientHandler() {
}
#Override
public boolean acceptInboundMessage(Object msg) throws Exception {
return true;
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info(msg.toString());
ctx.write(new Title());
}
#Override
protected void channelRead0(ChannelHandlerContext chc, Title title) throws Exception {
log.info(title.toString());
chc.write(new Title());
}
}
server output:
BUILD SUCCESSFUL
Total time: 3 seconds
Jul 26, 2014 8:10:24 PM io.netty.handler.logging.LoggingHandler channelRegistered
INFO: [id: 0x15d5077e] REGISTERED
Jul 26, 2014 8:10:24 PM io.netty.handler.logging.LoggingHandler bind
INFO: [id: 0x15d5077e] BIND(0.0.0.0/0.0.0.0:4454)
Jul 26, 2014 8:10:24 PM io.netty.handler.logging.LoggingHandler channelActive
INFO: [id: 0x15d5077e, /0:0:0:0:0:0:0:0:4454] ACTIVE
Jul 26, 2014 8:13:31 PM io.netty.handler.logging.LoggingHandler logMessage
INFO: [id: 0x15d5077e, /0:0:0:0:0:0:0:0:4454] RECEIVED: [id: 0xbcbabb1e, /127.0.0.1:34604 => /127.0.0.1:4454]
^Cthufir#dur:~/NetBeansProjects/AgentServer$
thufir#dur:~/NetBeansProjects/AgentServer$
The server seems to acknowledge a connection from the client (and can send and receive with a headless version).
The question is:
how do I send objects to the GUI, or get objects from the GUI? The TitlesGUI class extends JFrame.
Why doesn't the client receive the POJO which the server sends?
this blog sample is a bit difficult for me to follow.
I realize it's a lot of code, but I don't know how to make slim this down while still using a POJO, in this case Quote, between the client and server. The server sends a quote when a connection is established:
run:
[java] Aug 03, 2014 5:32:20 PM net.bounceme.dur.netty.QuoteServerInitializer <init>
[java] INFO: ..initializing..
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteServerInitializer initChannel
[java] INFO: ..adding to pipeline..
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteServerHandler <init>
[java] INFO: ..started..
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteServerHandler channelActive
[java] INFO: ..sending new server Quote..
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteEncoder encode
[java] INFO:
[java]
[java] id 0
[java] quote Where there is love there is life.
^Cthufir#dur:~/NetBeansProjects/QuoteServer$
thufir#dur:~/NetBeansProjects/QuoteServer$ ^C
thufir#dur:~/NetBeansProjects/QuoteServer$
but it never seems to arrive at the client:
run:
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteClientInitializer <init>
[java] INFO: ..initializing..
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteClientHandler channelActive
[java] INFO: ..sending new client Quote..
[java] Aug 03, 2014 5:32:23 PM net.bounceme.dur.netty.QuoteEncoder encode
[java] INFO:
[java]
[java] id 0
[java] quote client
^Cthufir#dur:~/NetBeansProjects/QuoteClient$
thufir#dur:~/NetBeansProjects/QuoteClient$
Similarly, the quote which the client sends never seems to make it to the server. Why?
server:
package net.bounceme.dur.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import java.util.logging.Logger;
public final class QuoteServer {
private static final Logger log = Logger.getLogger(QuoteServer.class.getName());
public static void main(String... args) throws InterruptedException {
MyProps p = new MyProps();
int port = p.getServerPort();
new QuoteServer().pingPong(port);
}
private void pingPong(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(new QuoteServerInitializer());
b.bind(port).sync().channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
server initializer:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import java.util.logging.Logger;
public class QuoteServerInitializer extends ChannelInitializer<SocketChannel> {
private static final Logger log = Logger.getLogger(QuoteServerInitializer.class.getName());
public QuoteServerInitializer() {
log.info("..initializing..");
}
#Override
public void initChannel(SocketChannel ch) throws Exception {
log.info("..adding to pipeline..");
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new QuoteDecoder());
pipeline.addLast(new QuoteEncoder());
pipeline.addLast(new QuoteServerHandler());
}
}
server handler:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.Random;
import java.util.logging.Logger;
import net.bounceme.dur.jdbc.Quote;
public class QuoteServerHandler extends SimpleChannelInboundHandler<Quote> {
private static final Logger log = Logger.getLogger(QuoteServerHandler.class.getName());
private static final Random random = new Random();
public QuoteServerHandler() {
log.info("..started..");
}
// 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 Quote nextQuote() {
int quoteId;
synchronized (random) {
quoteId = random.nextInt(quotes.length);
}
return new Quote(quotes[quoteId]);
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("..sending new server Quote..");
ctx.writeAndFlush(nextQuote());
}
#Override
protected void channelRead0(ChannelHandlerContext chc, Quote quote) throws Exception {
log.info(quote.toString());
chc.writeAndFlush(nextQuote());
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
log.info(msg.toString());
ctx.writeAndFlush(nextQuote());
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.flush();
}
}
client:
package net.bounceme.dur.netty;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.io.IOException;
import java.util.logging.Logger;
public final class QuoteClient {
private static final Logger log = Logger.getLogger(QuoteClient.class.getName());
public static void main(String... args) throws InterruptedException, IOException {
new QuoteClient().connect();
}
public void connect() throws InterruptedException, IOException {
MyProps p = new MyProps();
String host = p.getHost();
int port = p.getServerPort();
pingPong(host, port);
}
public void pingPong(String host, int port) throws InterruptedException, IOException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new QuoteClientInitializer());
ChannelFuture cf = b.connect(host, port);
cf.sync().channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
}
client initializer:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import java.util.logging.Logger;
public class QuoteClientInitializer extends ChannelInitializer<SocketChannel> {
private static final Logger log = Logger.getLogger(QuoteClientInitializer.class.getName());
public QuoteClientInitializer() {
log.info("..initializing..");
}
#Override
public void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(new QuoteDecoder());
pipeline.addLast(new QuoteEncoder());
pipeline.addLast(new QuoteClientHandler());
}
}
client handler:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.util.logging.Logger;
import net.bounceme.dur.jdbc.Quote;
#Sharable
public class QuoteClientHandler extends SimpleChannelInboundHandler<Quote> {
private static final Logger log = Logger.getLogger(QuoteClient.class.getName());
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("..sending new client Quote..");
ctx.writeAndFlush(new Quote("client"));
}
#Override
protected void channelRead0(ChannelHandlerContext chc, Quote quote) throws Exception {
log.info(quote.toString());
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
log.info(msg.toString());
ctx.writeAndFlush(new Quote("client"));
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.fireChannelReadComplete();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.info(cause.toString());
ctx.close();
}
}
decoder:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
import java.util.logging.Logger;
import net.bounceme.dur.jdbc.Quote;
public class QuoteDecoder extends MessageToMessageDecoder<Quote> {
private static final Logger log = Logger.getLogger(QuoteDecoder.class.getName());
#Override
protected void decode(ChannelHandlerContext chc, Quote quote, List<Object> list) throws Exception {
log.info(quote.toString());
list.add(quote);
}
}
encoder:
package net.bounceme.dur.netty;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.util.List;
import java.util.logging.Logger;
import net.bounceme.dur.jdbc.Quote;
public class QuoteEncoder extends MessageToMessageEncoder<Quote> {
private static final Logger log = Logger.getLogger(QuoteEncoder.class.getName());
#Override
protected void encode(ChannelHandlerContext chc, Quote quote, List<Object> list) throws Exception {
log.info(quote.toString());
list.add(quote);
}
}
It's quite notable that the en/de-code methods never log to the console.
If you edit the channelActive method of your QuoteServerHandler to the following:
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("..sending new server Quote..");
ChannelFuture cf = ctx.writeAndFlush(nextQuote());
if (!cf.isSuccess()){
log.log(Level.SEVERE, cf.toString());
}
ctx.fireChannelActive();
}
then you will most probably get a message saying: unsupported message type: Quote
Your encoder needs to encode it into something that is supported. I don't now what that would be though.
I would suggest using an ObjectEncoder which could encode your Quote into a ByteBuf.
On the receiving site you need an ObjectDecoder. After that you can cast your received msg in your ClientHandler back to Quote.
Using JBOSS Netty, I'm trying to send data continuously to the connected client. In the example below,
I try to send the time every 5 secs to the client, as soon as the client gets connected (channelConnected).
But this is not working. It works only if I comment the while loop.
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringEncoder;
public class SRNGServer {
public static void main(String[] args) throws Exception {
// Configure the server.
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// Configure the pipeline factory.
bootstrap.setPipelineFactory(new SRNGServerPipelineFactoryP());
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
}
private static class SRNGServerHandlerP extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger.getLogger(SRNGServerHandlerP.class.getName());
#Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Send greeting for a new connection.
e.getChannel().write("Welcome to " + InetAddress.getLocalHost().getHostName() + "!\r\n");
while(true){
e.getChannel().write("It is " + new Date() + " now.\r\n");
Thread.sleep(1000*5);
}
}
#Override
public void exceptionCaught(
ChannelHandlerContext ctx, ExceptionEvent e) {
logger.log(
Level.WARNING,
"Unexpected exception from downstream.",
e.getCause());
e.getChannel().close();
}
}
private static class SRNGServerPipelineFactoryP implements ChannelPipelineFactory {
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SRNGServerHandlerP());
return pipeline;
}
}
}
The Netty documentation actually states that you should never make a Handler wait because it might eventually deadlock. The reason is that handler methods are called directly by I/O threads. One I/O thread in Netty performs multiple I/O operations in a sequence, so it's not one thread per operation.
In the channelConnected method you should start a new thread with a reference to the channel and make that thread send the time every 5 seconds. This would spawn one thread per connection.
Alternatively, you can have one single thread looping over a list of clients every 5 seconds and sending the time to each of them in a sequence.
Anyway, it's important to use a different thread for sending than the one that calls the Handler.
For what its worth, I figured the solution and here's the working code. After the "write" of time, I register the future with my ChannelFuturelistener. And then from operationComplete I keep registering the new future for every write. This works for what I want to accomplish, without using any extra threads.
import java.net.InetSocketAddress;
import java.nio.channels.ClosedChannelException;
import java.util.Date;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.string.StringEncoder;
public class SRNGServer {
public static void main(String[] args) throws Exception {
// Configure the server.
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
//Executors.newCachedThreadPool()
Executors.newFixedThreadPool(2),2
));
// Configure the pipeline factory.
bootstrap.setPipelineFactory(new SRNGServerPipelineFactoryP());
// Bind and start to accept incoming connections.
bootstrap.bind(new InetSocketAddress(8080));
}
private static class SRNGServerHandlerP extends SimpleChannelUpstreamHandler {
private static final Logger logger = Logger.getLogger(SRNGServerHandlerP.class.getName());
#Override
public void channelConnected(
ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Send greeting for a new connection.
Channel ch=e.getChannel();
ChannelFuture writeFuture=e.getChannel().write("It is " + new Date() + " now.\r\n");
SRNGChannelFutureListener srngcfl=new SRNGChannelFutureListener();
writeFuture.addListener(srngcfl);
}
#Override
public void exceptionCaught(
ChannelHandlerContext ctx, ExceptionEvent e) {
logger.log(
Level.WARNING,
"Unexpected exception from downstream.",
e.getCause());
if(e.getCause() instanceof ClosedChannelException){
logger.log(Level.INFO, "****** Connection closed by client - Closing Channel");
}
e.getChannel().close();
}
}
private static class SRNGServerPipelineFactoryP implements ChannelPipelineFactory {
public ChannelPipeline getPipeline() throws Exception {
// Create a default pipeline implementation.
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("encoder", new StringEncoder());
pipeline.addLast("handler", new SRNGServerHandlerP());
return pipeline;
}
}
private static class SRNGChannelFutureListener implements ChannelFutureListener{
public void operationComplete(ChannelFuture future) throws InterruptedException{
Thread.sleep(1000*5);
Channel ch=future.getChannel();
if(ch!=null && ch.isConnected()){
ChannelFuture writeFuture=ch.write("It is " + new Date() + " now.\r\n");
//-- Add this instance as listener itself.
writeFuture.addListener(this);
}
}
}
}
Seems that the I/O thread is getting blocked as a result of sleep, so try using 2 worker threads instead:
ServerBootstrap bootstrap = new ServerBootstrap(
new NioServerSocketChannelFactory( Executors.newCachedThreadPool(),
Executors.newCachedThreadPool(), 2 ) );