I'm going through the default manual link and i encountered a problem. My echo server sends messages to the client, but i don't see them. As a telnet program i use putty.
The code is the same:
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
ChannelFuture cf = ctx.write(msg);
ctx.flush();
if (!cf.isSuccess()) {
System.out.println("Send failed: " + cf.cause());
}else{
System.out.println("Send worked.");
}
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
And second class:
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7)
// 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();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscardServer(port).run();
}
}
cf.isSuccess() is true, but in console (putty), i don't see anything. If i'm trying to send just text
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty MAY rock!", CharsetUtil.UTF_8));
it works. But if i tried to send "msg" - i get nothing.
Thanks in advance for your reply.
To read and write a non-ByteBuf message you need a decoder and encoder.
ch.pipeline().addLast(new LineBasedFrameDecoder(80))
.addLast(new StringDecoder())
.addLast(new StringEncoder())
.addLast(new DiscardServerHandler());
Or you can decode and encode message manually. To encode String into ByteBuf
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty MAY rock!", CharsetUtil.UTF_8));
The same code which you have mentioned here is working as it is. Tested it.
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) {
}
}
}
I'm learning Netty and prototyping a simple app which sends an object over TCP. My issue is that when I call Channel.write from the server side with my message, it doesn't seem to reach the handlers in the pipeline. When I send a message from client to server, it works as expected.
Here's the code.
The server:
public class Main {
private int serverPort;
private EventLoopGroup bossGroup;
private EventLoopGroup workerGroup;
private ServerBootstrap boot;
private ChannelFuture future;
private SomeDataChannelDuplexHandler duplex;
private Channel ch;
public Main(int serverPort) {
this.serverPort = serverPort;
}
public void initialise() {
boot = new ServerBootstrap();
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
boot.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(0, 0, 2));
// Inbound
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 0));
ch.pipeline().addLast(new SomeDataDecoder());
// Outbound
ch.pipeline().addLast(new LengthFieldPrepender(2));
ch.pipeline().addLast(new SomeDataEncoder());
// In-Out
ch.pipeline().addLast(new SomeDataChannelDuplexHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
}
public void sendMessage() {
SomeData fd = new SomeData("hello", "localhost", 1234);
ChannelFuture future = ch.writeAndFlush(fd);
future.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
System.out.println("send error: " + future.cause().toString());
} else {
System.out.println("send message ok");
}
}
});
}
public void startServer(){
try {
future = boot.bind(serverPort)
.sync()
.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
ch = future.channel();
}
});
} catch (InterruptedException e) {
// log failure
}
}
public void stopServer() {
workerGroup.shutdownGracefully()
.addListener(e -> System.out.println("workerGroup shutdown"));
bossGroup.shutdownGracefully()
.addListener(e -> System.out.println("bossGroup shutdown"));
}
public static void main(String[] args) throws InterruptedException {
Main m = new Main(5000);
m.initialise();
m.startServer();
final Scanner scanner = new Scanner(System.in);
System.out.println("running.");
while (true) {
final String input = scanner.nextLine();
if ("q".equals(input.trim())) {
break;
} else {
m.sendMessage();
}
}
scanner.close();
m.stopServer();
}
}
The duplex channel handler:
public class SomeDataChannelDuplexHandler extends ChannelDuplexHandler {
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("duplex channel active");
ctx.fireChannelActive();
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("duplex channelRead");
if (msg instanceof SomeData) {
SomeData sd = (SomeData) msg;
System.out.println("received: " + sd);
} else {
System.out.println("some other object");
}
ctx.fireChannelRead(msg);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
#Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.ALL_IDLE) { // idle for no read and write
System.out.println("idle: " + event.state());
}
}
}
}
And finally the encoder (the decoder is similar):
public class SomeDataEncoder extends MessageToByteEncoder<SomeData> {
#Override
protected void encode(ChannelHandlerContext ctx, SomeData msg, ByteBuf out) throws Exception {
System.out.println("in encoder, msg = " + msg);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(msg.getName());
oos.writeObject(msg.getIp());
oos.writeInt(msg.getPort());
oos.close();
byte[] serialized = bos.toByteArray();
int size = serialized.length;
ByteBuf encoded = ctx.alloc().buffer(size);
encoded.writeBytes(bos.toByteArray());
out.writeBytes(encoded);
}
}
The client side:
public class Client {
String host = "10.188.36.66";
int port = 5000;
EventLoopGroup workerGroup = new NioEventLoopGroup();
ChannelFuture f;
private Channel ch;
public Client() {
}
public void startClient() throws InterruptedException {
Bootstrap boot = new Bootstrap();
boot.group(workerGroup);
boot.channel(NioSocketChannel.class);
boot.option(ChannelOption.SO_KEEPALIVE, true);
boot.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
// Inbound
ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 0));
ch.pipeline().addLast(new SomeDataDecoder());
// Outbound
ch.pipeline().addLast(new LengthFieldPrepender(2));
ch.pipeline().addLast(new SomeDataEncoder());
// Handler
ch.pipeline().addLast(new SomeDataHandler());
}
});
// Start the client
f = boot.connect(host, port).sync();
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("connected to server");
ch = f.channel();
}
});
}
public void stopClient() {
workerGroup.shutdownGracefully();
}
private void writeMessage(String input) {
SomeData data = new SomeData("client", "localhost", 3333);
ChannelFuture fut = ch.writeAndFlush(data);
fut.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
System.out.println("send message");
}
});
}
public static void main(String[] args) throws InterruptedException {
Client client = new Client();
client.startClient();
System.out.println("running.\n\n");
final Scanner scanner = new Scanner(System.in);
while (true) {
final String input = scanner.nextLine();
if ("q".equals(input.trim())) {
break;
} else {
client.writeMessage(input);
}
}
scanner.close();
client.stopClient(); //call this at some point to shutdown the client
}
}
and the handler:
public class SomeDataHandler extends SimpleChannelInboundHandler<SomeData> {
private ChannelHandlerContext ctx;
#Override
public void channelActive(ChannelHandlerContext ctx) {
System.out.println("connected");
this.ctx = ctx;
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, SomeData msg) throws Exception {
System.out.println("got message: " + msg);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
System.out.println("caught exception: " + cause.getMessage());
ctx.close();
}
}
When I send a message via the console on the server side, I get the output:
running.
duplex channel active
duplex read
idle: ALL_IDLE
idle: ALL_IDLE
send message ok
So it looks as though the message is sent but nothing is received on the client side.
When I do it from the client side I get (on the server console):
in decoder, numBytes in message = 31
duplex channelRead
received: SomeData [name=client, ip=localhost, port=3333]
which is what I expect.
So where's the problem? Is it something to do with using a ChannelDuplexHandler on the server side and a SimpleChannelInboundHandler on the client side? Is there something I need to call to kick the message down the pipeline?
UPDATE
I've added a check for future.isSuccess() in the server sendMessage method and I get
send error: java.lang.UnsupportedOperationException on the console.
(Posted on behalf of the OP).
For anyone who's interested, the problem was that I was trying to send the message on the server channel and not the normal channel. This post pointed me in the right direction.
I am trying to figure out how to open a connection with netty and send the server some data.
This is my code I'm attempting to get working:
public static void main(String[] args) {
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 InboundHandler());
ch.pipeline().addLast(new OutboundHandler());
}
});
Channel channel = b.connect("localhost", 22000).channel();
channel.write(true); //Lets send a boolean to test.
} catch (Exception e) {
e.printStackTrace();
} finally {
workerGroup.shutdownGracefully();
}
}
I've tried sending the data as well when my inboundhandler is registered. Not sure what I am doing wrong.
Add logging handler to your pipeline to debug your data exchange:
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addFirst(new LoggingHandler(LogLevel.INFO));
ch.pipeline().addLast(new InboundHandler());
ch.pipeline().addLast(new OutboundHandler());
}
You should add listener to react on connection complete event like this:
ChannelFuture cf = b.connect("localhost", 22000);
cf.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
if(future.isSuccess()) {
//Here wrap your data into ByteBuf
future.channel().writeAndFlush(Unpooled.buffer().writeByte(5));
} else {
//Connection failed, add proper error handling !
future.cause().printStackTrace(System.err);
}
}
});
Confirmed that data was received:
strace -e recvfrom nc -k -l 22000
09:16:18.222252 recvfrom(5, "\x05", 8192, 0, NULL, NULL) = 1
09:16:18.223916 recvfrom(5, "", 8192, 0, NULL, NULL) = 0
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.
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 ;)