Netty NIO: Read received messages - java

I am developing a client and server communication system using Netty NIO in Java. My code can be found in the following repository. Currently I am having one server and two clients and I am sending information from server to the clients and the opposite.
What I am trying to figure out, when I am receiving a message form the first client to the server, how can i send that message to the second client (and the opposite from client 2 to client 1). How can I send a message to a specific client?
I have noticed that my issues arised because of the way that I am trying to send the messages from the server. My code in serverHandler is the following:
for (Channel ch : channels1) {
responseData.setIntValue(channels1.size());
remoteAddr.add(ch.remoteAddress().toString());
future = ch.writeAndFlush(responseData);
//future.addListener(ChannelFutureListener.CLOSE);
System.out.println("the requested data from the clients are: "+requestData);
responseData1.setStringValue(requestData.toString());
future = ch.writeAndFlush(responseData1);
System.out.println(future);
}
By default am sending a message about the number of the connections, but also when I am receiving message from the client 1 or 2 I want to send it back to 2 and 1. So I want to perform the communication between the two components. How can I send from the server to a specific client? I am not sure how can I send the messages back to the clients.

General approach
Let's describe an approach to the problem.
When receiving data on the server side, use the remote address of the channel (the java.net.SocketAddress Channel.remoteAddress() method) to identify the client.
Such identification may be done using a map like: Map<SocketAddress, Client>, where the Client class or interface should contain the appropriate client connection (channel) associated context, including its Channel. Be sure to keep the map up-to-date: handle the «client connected» and «client disconnected» events appropriately.
After a client is identified, you may just send the appropriate messages to the clients, except the current sending client, using the client connection (channel) map.
Additionally, I would like to recommend you to find a good implementation of a chat application using Netty and to take a look at it.
Netty-specific solution
Let's consider the server side implementation, in particular, the implementation of the ProcessingHandler class.
It already manages the active channels by representing them as the channel group:
static final ChannelGroup channels1 =
new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
Keeping the channel group up-to-date
The current implementation handles the «channel becomes active» event to keep the channel group up-to-date:
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channels1.add(ctx.channel());
// ...
}
But this is only a half: it is necessary to handle the «channel becomes inactive» event symmetrically as well. The implementation should look like:
#Override
public void channelInactive(final ChannelHandlerContext ctx) throws Exception {
channels1.remove(ctx.channel());
}
Broadcasting: Sending the received message to all channels, except the current one
To implement the desired behaviour, just update the implementation by introducing the appropriate check as follows:
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
// ...
for (Channel ch : channels1) {
// Does `ch` represent the channel of the current sending client?
if (ch.equals(ctx.channel())) {
// Skip.
continue;
}
// Send the message to the `ch` channel.
// ...
}
// ...
}
Sending and receiving string problem
Currently, the functionality around the ResponseData class is not present (not implemented).
The following draft changes are required to make both the client and the server work.
The ResponseData class: the getStringValue and toString methods should be corrected:
String getStringValue() {
return this.strValue;
}
#Override
public String toString() {
return intValue + ";" + strValue;
}
The ResponseDataEncoder class: it should use the string value:
private final Charset charset = Charset.forName("UTF-8");
#Override
protected void encode(final ChannelHandlerContext ctx, final ResponseData msg, final ByteBuf out) throws Exception {
out.writeInt(msg.getIntValue());
out.writeInt(msg.getStringValue().length());
out.writeCharSequence(msg.getStringValue(), charset);
}
The ResponseDataDecoder class: it should use the string value:
private final Charset charset = Charset.forName("UTF-8");
#Override
protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final List<Object> out) throws Exception {
ResponseData data = new ResponseData();
data.setIntValue(in.readInt());
int strLen = in.readInt();
data.setStringValue(in.readCharSequence(strLen, charset).toString());
out.add(data);
}
The ClientHandler class: it should correctly receive and handle the message:
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
final ResponseData responseData = (ResponseData) msg;
System.out.println("The message sent from the server " + responseData);
update.accept(responseData.getIntValue());
}
Additional references
«SecureChat ‐ an TLS-based chat server, derived from the Telnet example», Netty Documentation. In particular, the implementation of the SecureChatServerHandler class.
«Netty in Action», Norman Maurer, Marvin Allen Wolfthal (ISBN-13: 978-1617291470), «Part 3 — Network protocols», the «12.2 Our example WebSocket application» subchapter. Covers implementation of «a browser-based chat application».

Related

Is it safe to store the remote address of a DatagramPacket in the Channel AttributeMap in Netty?

I have a UDP inbound connection. I need to know the address of the sender of the packet in one of the ChannelHandlers near the end of the pipeline. Is it safe/correct to store it in the channel's AttributeMap? Or do I risk the value from one packet being overwritten by a later packet before my handler can process it? Or is there a better/standard way to do this?
My first handler in the pipeline is basically just this:
public class DatagramToBytesInboundChannelHandler
extends MessageToMessageDecoder<DatagramPacket> {
public static final AttributeKey<InetSocketAddress> REMOTE_ADDRESS =
AttributeKey.newInstance( "UdpRemoteAddress" );
#Override
protected void decode( ChannelHandlerContext ctx, DatagramPacket msg,
List<Object> out ) throws Exception {
ctx.channel().attr( REMOTE_ADDRESS ).set( msg.sender() );
out.add( msg.content().copy() );
}
}
Then in a channel handler later (there are many, I'm just showing one of interest) in the pipeline, I need to retrieve it:
public class MyChannelHandler extends
SimpleChannelInboundHandler<MyMessage> {
#Override
protected void channelRead0( ChannelHandlerContext ctx, MyMessage msg ) {
InetSocketAddress remoteAddress = ctx.channel().attr(
REMOTE_ADDRESS ).getAndSet( null );
// Do things with the remote address
}
}
Is there any risk if I get two packets in quick succession, one from A and the next from B, that when MyHandler is processing the message from A that it will see the address from B?
Does the answer to this depend on my Bootstrap configuration at all?

Netty - can not write to channel at registration (channelRegistered event)

I've encountered a problem concerning sending packets at channel registration (Client -> Server).
To test furthermore, if I send the packet after the registration, I am allowed to send the packet.
The opposite way (Server -> Client) works totally fine.
Registration function:
#Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
ctx.channel().write(new byte[] {0x00});
}
It also encounters an encoder down the filter chain:
public class Encoder extends MessageToByteEncoder<byte[]>{
#Override
protected void encode(ChannelHandlerContext ctx, byte[] b, ByteBuf bb) throws Exception {
byte[] data = b.getData();
bb.writeInt(data.length);
bb.writeBytes(data);
}
}
The server therefore does not receive the aforementioned packet - as a result,
I debugged to see if channelRead event fired and it did not. (Neither did my decode class to decode the data)
Any assumptions ?
channelRegistered is called when the channel is registered to it's EventLoop. That does not imply it is ready to send data yet, the channel hasn't even opened a socket yet.
You want channelActive, which is called when the channel is active, i.e. ready to send and receive data.

Jetty WebSocket Client Doesn't work for Binary Listener

I have a Java app that uses the Jetty WebSocket Client, version 9.x. It works fine for text messages sent from the server, but the binary listener is never invoked. I have a Javascript client implementation which I'm basically duplicating. I'm doing the same exact thing in Javascript that I do in Java, calling the same server. The Javascript works, and the Java doesn't. So I'm thinking that something is not configured properly in Jetty for binary listeners.
For example, the server is sending blob data. I know that in the Javascript client, I can set the binarytype to either arraybuffer or blob. I figured there may be a similar setting required in Jetty, but I've looked all through the API and searched many examples online. There are precious few examples of binary listeners online, and no mention anywhere of setting the binarytype, or any other special setting required to make binary llisteners work.
Here's a consolidated representation of my code. The code is spread throughout various classes, so this is not a stand-alone app, but I think it shows what I'm doing. The server is implemented with libwebsockets.
Client implementation
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
client = new WebSocketClient();
client.start();
client.setMaxBinaryMessageBufferSize((int) 500e6);//just to be sure
ClientUpgradeRequest request = new ClientUpgradeRequest();
request.setSubProtocols("pipe-data");
client = new SimpleSocket();
client.connect(socket, uri, request);
Socket implementation
#WebSocket
public class SimpleSocket {
#SuppressWarnings("unused")
private Session session;
private SocketHandlerBase handler;
private boolean connected = false;
public SimpleSocket(SocketHandlerBase listener) {
this.handler = listener;
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
this.handler.onClose(statusCode, reason);
this.connected = false;
}
#OnWebSocketConnect
public void onConnect(Session session) {
this.handler.onConnect(session);
this.connected = true;
}
//invoked when text messages are sent
#OnWebSocketMessage
public void onMessage(String msg) {
this.handler.onMessage(msg);
}
//does not get invoked when binary data is sent
#OnWebSocketMessage
public void onMessage(byte buf[], int offset, int length) {
this.handler.onMessage(buf, offset, length);
}
public boolean isConnected() {
return this.connected;
}
public SocketHandlerBase getHandler() {
return this.handler;
}
}
There was a hard to find problem with the server I was calling. A very specific configuration of invocation arguments was causing the binary listener to not be called. Nothing about the Jetty client or WebSockets in general involved here.

data cache for odata4j

I am implementing a RESTlet service via odata4j on Android.
When running the application there is a constant flow of data, which needs to be sent to a odata server.
The following method get's the new data:
private void freshData(Data data) {
try {
dataTransmitter.sendData(data, this.ptId);
} catch (Exception ex) {
//
}
The following method sends the data to the server:
ODataJerseyConsumer c = ODataJerseyConsumer.create(serviceUrl);
public void sendData(Data data, int ptId) throws Exception {
OEntity newData = c.createEntity(entitySet)
.properties(OProperties.int32("ptID", ptID),
OProperties.double_("data", data.getDouble())))
.execute;
So far no problem. But what if the mobile connection is cut off or lags?
1) Is there way to get the status from execute() (positive send, or no mobile connection for instance).
2) If the send has failed, or is still in progress I somehow need to store the new data to send it, when the old data is out. Is there a feature in odata4j (0.8-SNAPSHOT) available I haven't found, or do I need to do this with a queue for example?
Thank you for the help!

Java Client Server Programming: How to pass message from server to All Client threads?

I am creating a program with a server A and multiple clients B, C, D.
B C & D will all message the client with a number X, and I would like to know how it is possible for the server to message ALL clients simultaneously with the latest value for X?
As it stands, it will update only the client who has last passed number X.
Here is the code I have for run()
public void run(){
String number;
do
{
//Accept message from client on
//the socket's input stream...
received = in.readLine();
//Echo message back to client on
//the socket's output stream...
out.println("Number recieved: " + number);
}
}
Google up JMS Publish and Subscribe.
Basically:
The server publishes to a topic and the clients subscribe to a topic.
The best way to notify clients about something is to use JMX. If you're not supposed to use this technology, then you should keep clients list somewhere in your code (say in static field) and then iterate over this list and send received number
I'm not sure what you're trying to do...but you could try broadcasting a message using socket programming. Check this out
You can add all the sockets to a collection. Send the same message to every socket in the collection. Remove sockets from the collection when they are closed.
e.g.
final List<Socket> sockets = new CopyOnWriteArrayList<Socket>();
// when you have a new socket
sockets.add(socket);
// when you have a dead socket.
sockets.remove(socket);
// to send the same message to multiple sockets.
public static void sendToAll(byte[] bytes) {
for(Socket s: sockets)
try {
s.getOutputStream().write(bytes);
} catch (IOException ioe) {
// handle exception, close the socket.
sockets.remove(s);
}
}
I agree the real solution is JMS, but if you want to "roll your own" a simple solution I would suggest is making your own simplified version using the same idea of JMS. Create a class that will receive events from your client. Create an interface that your clients can implement and then add themselves as a listener to this new class. Some simple code:
class MyEventPublisher {
Collection<EventListener> listeners;
int number;
public void addListener(EventListener listener) {
listeners.add(listener);
}
public void setNumber(int newNumber) {
int oldNumber = this.number;
this.number = newNumber;
for (EventListener listener : listeners) {
listener.numberChanged(newNumber, oldNumber);
}
}
}
interface EventListener {
void numberChanged(int newNumber, int oldNumber);
}
class MyClientSocket implements EventListener {
MyEventPublisher publisher;
public MyClientSocket(MyEventPublisher publisher) {
this.publisher = publisher;
publisher.addListener(this);
}
public recieveNumberFromSocket() {
int numberFromSocket = readNumber();
publisher.setNumber(numberFromSocket);
}
public void numberChanged(int newNumber, int oldNumber) {
//someone else changed the number
//do something interesting with it
}
}
You are looking for a multicast protocol, based on your descriptions.
So, I'll guess you'll be better of looking this:
Multicast (JDK 6)
Multicast (JDK 7)
Previous versions starting from JDK version 1.4.2 include multicast but you'll be better off if you use JDK version 6 or greater ;)

Categories

Resources