Netty TCP Server is running at port 8000 receiving NMEA format data. It uses Marine API library to convert the gibberish to a meaningful information which needs input stream from the socket.
SentenceReader sentenceReader = new SentenceReader(socket.getInputStream());
sentenceReader.addSentenceListener(new MultiSentenceListener());
sentenceReader.start();
How can i get inputstream for netty server port being used?
SentenceReader does not have any method to accept "streamed in" data, however with subclassing, it can be made to accept the data.
The core of SentenceReader uses a DataReader for its data, normally this datareader is polled from a seperate thread SentenceReader itself, and we can modify this structure to get what we need.
First, we subclass SentenceReader with our own class, give it the proper constructor and methods we want, and remove the effect of the start and stop methods. We provide null as the file for now (and hope future versions provide a method to pass a datareader in directly)
public class NettySentenceReader extends SentenceReader {
public NettySentenceReader () {
super((InputStream)null);
}
#Override
public void start() {
}
#Override
public void stop() {
}
}
We now need to implement all functionality of the internal class DataReader inside our own Netty handler, to replicate the same behaviour
public class SentenceReaderHandler extends
SimpleChannelInboundHandler<String> {
private SentenceFactory factory;
private SentenceReader parent;
public SentenceReaderHandler (SentenceReader parent) {
this.parent = parent;
}
#Override
public void channelRegistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
// This method will be renamed to `messageReceived` in Netty 5.0.0
protected void channelRead0(ChannelHandlerContext ctx, String data)
throws Exception {
if (SentenceValidator.isValid(data)) {
monitor.refresh();
Sentence s = factory.createParser(data);
parent.fireSentenceEvent(s);
} else if (!SentenceValidator.isSentence(data)) {
parent.fireDataEvent(data);
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void channelUnregistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
parent.handleException("Data read failed", e);
}
}
Finally, we need to integrate this into a Netty pipeline:
SentenceReader reader = new NettySentenceReader();
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
private static final StringDecoder DECODER = new StringDecoder();
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(new SentenceReaderHandler(reader));
}
});
You can't easily as InputStream is blocking and netty is an async - non blocking API.
Related
I am using Netty v4.1.9final and trying to send a message from client to server. I tried setting up the channel client initializer with the handler as follows on the client side:
final Bootstrap bootstrap = BootstrapGenerator.generate();
bootstrap.handler(new XmlClientInitializer());
XMLClientInitializer
public class XmlClientInitializer extends ChannelInitializer<SocketChannel> {
#Override
public void initChannel(SocketChannel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("fileEncoder", new FileEncoder());
pipeline.addLast("handler", new XmlSenderHandler());
}
}
FileEncoder
public class FileEncoder extends MessageToByteEncoder<String> {
XmlSenderHandler
public class XmlSenderHandler extends ChannelOutboundHandlerAdapter {
private static final Logger log = LogManager.getLogger(XmlSenderHandler.class.getName());
private static ChannelHandlerContext ctx;
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
log.info("attempting to write messages to server {}", msg.toString());
ctx.write(msg, promise);
}
#Override
#SuppressWarnings("FutureReturnValueIgnored")
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
Even though the FileEncoder is defined first in the pipeline, it gets called after the xml handler (which is not what I am seeking). Is this because the FileEncoder is extending MessageToByteEncoder or am I configuring the channel incorrectly?
No, the pipeline is configured correctly. The point is that the inbound event is processed from the first handler to the last, and the outbound event is processed from the last handler to the first one.
ChannelPipeline doc
I am new to Apache Camel and trying to receive a simple SNMP trap.
I have the Maven project set up with camel-core and org.apache.servicemix.bundles.snmp4j.
I have not been able to find any SNMP examples, but based on other examples I have come up with this Main class:
public class Main {
public static Processor myProcessor = new Processor() {
public void process(Exchange arg0) throws Exception {
// save to database
}
};
public static void main(String[] args) {
CamelContext context = new DefaultCamelContext();
context.addComponent("snmp", new SnmpComponent());
RouteBuilder builder = new RouteBuilder() {
public void configure() {
from("snmp:127.0.0.1:162?protocol=udp&type=TRAP").process(myProcessor);
}
};
try {
context.addRoutes(builder);
context.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
However when I run it in Eclipse as Java application it just exits after running for half a second. I was expecting it to keep running and listening to 127.0.0.1:162 ...
Any help is greatly appreciated
One way to at least pick up a trap and print to System.out is like so:
public class SNMPTrap {
private Main main;
public static void main(String[] args) throws Exception {
SNMPTrap snmpTrap = new SNMPTrap();
snmpTrap.boot();
}
#SuppressWarnings("deprecation")
public void boot() throws Exception {
main = new Main();
main.bind("snmp", new SnmpComponent());
main.addRouteBuilder(new MyRouteBuilder());
main.addMainListener(new Events());
System.out.println("Starting SNMPTrap. Use ctrl + c to terminate the JVM.\n");
main.run();
}
private static class MyRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("snmp:127.0.0.1:162?protocol=udp&type=TRAP").process(myProcessor)
.bean("snmp");
}
}
public static Processor myProcessor = new Processor() {
public void process(Exchange trap) throws Exception {
System.out.println(trap.getIn().getBody(String.class));
// Save to DB or do other good stuff
}
};
public static class Events extends MainListenerSupport {
#Override
public void afterStart(MainSupport main) {
System.out.println("SNMPTrap is now started!");
}
#Override
public void beforeStop(MainSupport main) {
System.out.println("SNMPTrap is now being stopped!");
}
}
}
However, I get warning that Main which is part of Camel core is deprecated now.
I'm using netty to develop a proxy server and my proxy ProxyBackendHandler class is as follows. There on channelRead method I need to get the msg data and write to client as TextWebSocketFrame. To do that I have used a StringBuilder and a while loop to iterate the ByteBuf. Can anyone suggest me a better way to do this as it seems that above code has high perfomance overhead when the high data loads.
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
StringBuilder sReplyBuffer;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
sReplyBuffer = new StringBuilder(4000);
}
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
// Please suggest a efficient implementation for read msg and pass it to writeAndFlush.
ByteBuf in = (ByteBuf) msg;
sReplyBuffer.setLength(0);
try {
while (in.isReadable()) {
sReplyBuffer.append((char) in.readByte());
}
} finally {
((ByteBuf) msg).release();
}
inboundChannel.writeAndFlush(new TextWebSocketFrame (sReplyBuffer.toString())).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
System.out.println("Sent To Client");
} else {
future.channel().close();
}
}
});
}
}
Maybe something like this:
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
inboundChannel.writeAndFlush(new TextWebSocketFrame((ByteBuf) msg)).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
System.out.println("Sent To Client");
} else {
future.channel().close();
}
}
});
}
}
I suggest not using a StringBuilder at all. Just use the buffer you already have. You don't state what a TextWebSocketFrame might be, or why you think you need it, but ultimately a proxy server only has to copy bytes. You don't need StringBuilders or extra classes for that. Or Netty, frankly.
I've got a Play framework 2 application that can receive data and send it to multiple clients via WebSockets. I use Akka actors to work with WebSockets, just like in this documentation. I also have a WebSocketRouter class that extends UntypedActor and contains routing logic (decides, which clients to pass the data the system receives to). I know that i can use the Router functionality of Akka, but that is not the issue at the moment for me. The issue is that i have to store a list of all active clients. Right now i store it in a static list of the WebSocketRouter class. That was the fastest way to write a proof-of-concept prototype, but it is not thread-safe and does not seem to be "the Akka way".
Below is a simplified code sample:
WebSocketController:
//This controller handles the creation of WebSockets.
public class WebSocketController extends Controller {
public static WebSocket<String> index() {
return WebSocket.withActor(new F.Function<ActorRef, Props>() {
public Props apply(ActorRef out) throws Throwable {
return MessageSender.props(out);
}
});
}
}
MessageSender :
//Hold a reference to the auto-created Actor that handles WebSockets
//and also registers and unregisters itself in the router.
public class MessageSender extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MessageSender.class, out);
}
private final ActorRef out;
public MessageSender(ActorRef out) {
this.out = out;
}
#Override
public void preStart() {
WebSocketRouter.addSender(getSelf());
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, getSelf());
}
else {
unhandled(message);
}
}
public void postStop() {
WebSocketRouter.removeSender(getSelf());
}
}
WebSocketRouter:
public class WebSocketRouter extends UntypedActor {
private static ArrayList<ActorRef> senders;
static {
senders = new ArrayList<>();
}
public static void addSender(ActorRef actorRef){
senders.add(actorRef);
}
public static void removeSender(ActorRef actorRef){
senders.remove(actorRef);
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
for (ActorRef sender : senders) {
sender.tell(message, getSelf());
}
}
}
}
Once again, i know this is a bad solution and i'm seeking a better one. I have thought of creating a thread-safe singleton class that would hold current connections. I also thought about holding the list of current connections in an instance of some Akka actor and modifying the list via Akka messages, but for this way to work I'd have to store an ActorRef to that actor statically, so that it could be accessed from different ActorSystems.
What is the best way to solve my problem that would fit best into Akka ideology?
Instead of having a static reference to an Actor (WebSocketRouter in your case), why not come up with some messages to send it? That way, the actor can maintain its own internal state in a consistent way. State change through messages is one of the main benefits of the Actor Model.
Before I get into code, I'm sorry if this isn't 100% accurate, I've only used the Scala version of Akka and am basing this off a quick scan of the Akka Documentation.
So in your case, I would define a few objects in order to express Join/Leave...
public class JoinMessage { }
public class ExitMessage { }
Note that ExitMessage is really only needed if you intend to keep your WebSocket open and have the user stop listening to the router. Otherwise, the router can detect when the Actor has been terminated.
And then you would change your MessageSender actor to send these messages whenever they join or leave a chat room....
public class MessageSender extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MessageSender.class, out);
}
private final ActorRef out;
private final ActorRef router;
public MessageSender(ActorRef out) {
this.out = out;
this.router= getContext().actorSelection("/Path/To/WebSocketRouter");
}
#Override
public void preStart() {
router.tell(new JoinMessage(), getSelf());
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, getSelf());
} else {
unhandled(message);
}
}
}
And then your router can change to manage state internally rather than exposing internal methods on the Actor (which as you know is not good)....
public class WebSocketRouter extends UntypedActor {
private final Set<ActorRef> senders = new HashSet<>();
private void addSender(ActorRef actorRef){
senders.add(actorRef);
}
private void removeSender(ActorRef actorRef){
senders.remove(actorRef);
}
#Override
public void onReceive(Object message) throws Exception {
if (message instanceof JoinMessage) {
addSender(sender);
getContext().watch(sender); // Watch sender so we can detect when they die.
} else if (message instanceof Terminated) {
// One of our watched senders has died.
removeSender(sender);
} else if (message instanceof String) {
for (ActorRef sender : senders) {
sender.tell(message, getSelf());
}
}
}
}
Again, this code is to give you an idea of how to accomplish this task by taking advantage of the Actor Model. Sorry if the Java isn't 100% accurate, but hopefully you can follow my intent.
In Netty 3.5 we use SimpleChannelHandler, which provides method for both event types. How do I use the same approach in Netty 4.0.0?
To be more specific i m looking for a substitute of the method below
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
}
I am trying to send message to all clients connected to the server.
Here is the example for netty 3.x
ChannelGroup allConnected = new DefaultChannelGroup("all-connected");
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelConnected(ctx, e);
allConnected.add(e.getChannel());
}
and then to send messages to all channels connected
ChannelBuffer cb = ChannelBuffers.wrappedBuffer("hello".getBytes(Charset.forName("UTF-8")));
allConnected.write(cb);
This is what i need to do in Netty 4.21 final but i couldn't find a similar method which provided me the needed functionality.
I'm not really sure what you mean by both event types. I guess you mean client and server? I use ChannelInboundHandlerAdapter for that:
public class ServerCommunicationHandler extends ChannelInboundHandlerAdapter {
private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channels.add(ctx.channel());
ctx.fireChannelActive();
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
broadcastMessage(msg);
ctx.fireChannelRead(msg);
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
channels.remove(ctx.channel());
ctx.fireChannelInactive();
}
public void broadcastMessage(Object object) {
channels.writeAndFlush(object);
}
}