Netty (4) client server communication in the same TCP Session - java

I need to build a client that initiates a TCP connection with a server and upon response it sends a hand shake request every 10 seconds and gets a response from the server. The server will be able to send another type of request which my client needs to read and act upon. I am using netty 4.0.26.Final.
I have built a client and a dummy server but I am facing an issue which possibly means that there is something I have not understood.
My Client:
String host = "localhost";
int port = 9884;
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new MyChannelPipeline());
// Start the client.
ChannelFuture f = b.connect(host, port).sync();
String line = "line";
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
while (!line.equals("exit")) {
line = in.readLine();
if (line == null) {
break;
}
}
// Wait until the connection is closed.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
}
The ChannelPipleline:
#Override
public void initChannel(Channel ch) throws Exception {
ChannelPipeline channelPipeline = ch.pipeline();
//Encodes every request send from the client to the server
channelPipeline.addLast("clientRequestEncoder", new ClientRequestEncoder());
//Implements channelActive and exceptionCaught
channelPipeline.addLast("initialRequestHandler", new InitialRequestHandler());
channelPipeline.addLast("byteArrayDecoder", new ByteArrayDecoder());
channelPipeline.addLast("serverResponseDecoder", new ServerResponseDecoder());
channelPipeline.addLast("serverRequestDecoder", new ServerRequestDecoder());
//Reads the responses from the client requests AND
//reads the inbound requests from the server - Implements channelRead
//and exceptionCaught
channelPipeline.addLast("myResponseHandler", new MyResponseHandler());
}
The problem is that when I flush the response to the server (in MyResponseHandler) and exception is caught in InitialRequestHandler:
ERROR=java.lang.UnsupportedOperationException:unsupported message type: ServerResponse (expected: ByteBuf, FileRegion)
I don't see why the response is not flushed back to the server while the hand shake request is always properly flushed. In both write and flush I have used a ChannelFuture and onOperationComplete this listener f.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); is fired on failure.
Can I use two handlers in the same pipeline or is it bad practise? Moreover how should I fire an unregister event triggered by user input?

I solved this using one Handler that overrides channelActive and channelRead and I rearranged the encoders and decoders properly. I also solved the "unregister event triggered by user input" this way:
String line = "line";
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
do {
logger.info("You typed: " + line + ". Please type 'exit' to terminate the program!");
line = in.readLine();
} while (!line.equals("exit"));
logger.info("You typed: " + line + ". Please wait until the application is successfully shutdown...");
f.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
myChannelPipeline.getMyClientHandler().sendDisconnectRequest(future);
}
});
In sendDisconnectReqeust I send the final request and when I get the final response (in channelRead of MyHandler) I call disconnect on the pipeline:
ChannelPromise cp = new DefaultChannelPromise(ctx.channel());
ctx.channel().pipeline().disconnect(cp);
However I still have other issues with inbound requests that are never received by my client.

Related

Spring Integration : No publisher available to publish TCP Connection related events

I am using Spring Integration with Spring Boot. I have a TCP Client [TcpNetClientConnectionFactory] with TcpOutboundGateway setup. I can see the below warnings in Production[No publisher available to publish].
Log Snippet
Based on my checking this warning is shown when the org.springframework.context.ApplicationEventPublisher is null.
Code:
#Bean
#ServiceActivator(inputChannel = "a04A08OutgoingChannel")
public MessageHandler a04A08OutgoingGate() {
final TcpOutboundGateway gate = new TcpOutboundGateway();
// Connection configured in client mode to send the message over the TCP socket
// and wait for acknowledgement
gate.setConnectionFactory(connectionFactory.connectionFactory(host, port));
gate.setReplyChannelName("a04A08ReplyToString");
gate.setRemoteTimeout(60_000);
return gate;
}
#Transformer(inputChannel = "a04A08ReplyToString")
public String transform(byte[] bytes) {
String reply = new String(bytes);
log.debug("transform - a04A08ReplyToString channel " + reply);
return new String(bytes);
}
public String outgoingMessage(String message) {
String reply = null;
log.info("Message being Sent : " + message);
try {
// Send the message to the TCP socket and wait for acknowledgement
reply = a04a08OutgoingGateway.sendMessage(message);
} catch (ConnectException e) {
log.error(e.getMessage(),e);
}
log.info("Acknowledgement received : " + reply);
return reply;
}
ConnectionFactory.java:
public AbstractClientConnectionFactory connectionFactory(String host, int port) {
final AbstractClientConnectionFactory connectionFactory = new TcpNetClientConnectionFactory(host, port);
connectionFactory.setSerializer(customDeserializer);
connectionFactory.setDeserializer(customDeserializer);
//connectionFactory.setSoKeepAlive(true);
connectionFactory.setSingleUse(true);// This property when set to false ensures that one shared connection is used for all
// request/replies and each caller blocks waiting for the socket
return connectionFactory;
}
Edit 1 : Included CustomDeserializer.java
#Override
public void serialize(String object, OutputStream outputStream) throws IOException {
log.info("[Serialize] Serializing data : length ==> " + object.length());
outputStream.write(object.getBytes());
log.info("[Serialize] data posted to stream");
}
#Override
public byte[] deserialize(InputStream inputStream) throws IOException {
log.info("[Deserialize] De-Serializing data");
BufferedReader input = new BufferedReader(new InputStreamReader(inputStream));
StringBuffer stringbuffer = new StringBuffer();
while (true) {
int value = input.read();
if (value == 28) {
break;
} else {
if (value != -1) {
stringbuffer.append((char) value + "");
} else {
break;
}
}
}
log.info("[deserialize.readFromSocket()]: " + stringbuffer.toString());
return stringbuffer.toString().getBytes();
}
The TCP server is able to receive the messages sent by the TCP client. [Note: TCP server is a different system and not maintained by us].I have 2 queries.
Will this warning have any impact? Can anyone elaborate on the warning? Even when the warnings are seen the messages from TCP client are sent to TCP server without any issues.
We faced below issue (Caused by: org.springframework.messaging.MessagingException: Exception while awaiting reply; nested exception is java.net.SocketTimeoutException: Read timed out) in production recently. When we faced the below exception, telnet to the server port worked but the messages were not received by the server. The issue was automatically resolved when the TCP server was restarted. My question : Is this issue related to the warning in point #1. These warnings are seen even on normal days when the messages are sent to the server without any issues.
Error logs
P.S: I also checked the post : No publisher available to publish TcpConnectionOpenEvent / TcpConnectionCloseEvent
It is not related; sounds like a server problem if restarting it solves it.
The connection factory must be declared as a #Bean so that spring can inject the event publisher.

Java Sockets + socket.io-client (Angular 13) = Failed connections and scrambled messages

I am trying to use a Java Socket Server with socket.io-client, but it has an erratic behavior from the moment of the connection. It manages to stablish connection, but then this exception is thrown in Angular:
GET https://127.0.0.1:1532/socket.io/?EIO=4&transport=polling&t=N__rEfS net::ERR_SSL_PROTOCOL_ERROR
And the Server in Java only receives scrambled text repatedly
and the Client starts connecting and disconnecting over and over again. Why is this happening? Is there any way to get a cleaner Socket connection from Angular 13 to Java?
I use this Java Socket Server for many other applications and it works perfectly for everything else but this.
This is the routine that reads the Java Server:
void handleClientRequest() {
try{
mBufferIn = new BufferedReader(new InputStreamReader( socket.getInputStream()));
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//in this while the client listens for the messages sent by the server
while (clientRun) {
String clientMessage = mBufferIn.readLine();
if (clientMessage != null && mMessageListener != null) {
mMessageListener.messageReceived(clientMessage);
}
}
} catch(Exception e){
System.out.printf("%s: Unexpected client disconnection. Reason:%n", accountId);
e.printStackTrace();
}
}
And this is the Angular code:
this.socket = io('https://127.0.0.1:1532');
this.socket.on('connect', () => {
const engine = this.socket.io.engine;
console.log(engine.transport.name); // in most cases, prints "polling"
engine.once('upgrade', () => {
// called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
console.log(engine.transport.name); // in most cases, prints "websocket"
});
engine.on('packet', ({ }) => {
// called for each packet received
});
engine.on('packetCreate', ({ }) => {
// called for each packet sent
});
engine.on('drain', () => {
// called when the write buffer is drained
});
engine.on('close', (reason: any) => {
// called when the underlying connection is closed
});
});
Code taken from https://socket.io/docs/v4/client-socket-instance/
Socket IO is a communication protocol implemented on the top of websocket
from my understanding (correct me if im wrong) you are using raw socket in java.
So very likely that "scrambled" text that you are receiving, is part of https handshake.
My suggestion as way forward, will be to use library that handles websocket connections.

Netty Nio read the upcoming messages from ChannelFuture in Java

I am trying to use the following code which is an implementation of web sockets in Netty Nio. I have implment a JavaFx Gui and from the Gui I want to read the messages that are received from the Server or from other clients. The NettyClient code is like the following:
public static ChannelFuture callBack () throws Exception{
String host = "localhost";
int port = 8080;
try {
Bootstrap b = new Bootstrap();
b.group(workerGroup);
b.channel(NioSocketChannel.class);
b.option(ChannelOption.SO_KEEPALIVE, true);
b.handler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new RequestDataEncoder(), new ResponseDataDecoder(),
new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
}));
}
});
ChannelFuture f = b.connect(host, port).sync();
//f.channel().closeFuture().sync();
return f;
}
finally {
//workerGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
ChannelFuture ret;
ClientHandler obj = new ClientHandler(i -> {
synchronized (lock) {
connectedClients = i;
lock.notifyAll();
}
});
ret = callBack();
int connected = connectedClients;
if (connected != 2) {
System.out.println("The number if the connected clients is not two before locking");
synchronized (lock) {
while (true) {
connected = connectedClients;
if (connected == 2)
break;
System.out.println("The number if the connected clients is not two");
lock.wait();
}
}
}
System.out.println("The number if the connected clients is two: " + connected );
ret.channel().read(); // can I use that from other parts of the code in order to read the incoming messages?
}
How can I use the returned channelFuture from the callBack from other parts of my code in order to read the incoming messages? Do I need to call again callBack, or how can I received the updated message of the channel? Could I possible use from my code (inside a button event) something like ret.channel().read() (so as to take the last message)?
By reading that code,the NettyClient is used to create connection(ClientHandler ),once connect done,ClientHandler.channelActive is called by Netty,if you want send data to server,you should put some code here. if this connection get message form server, ClientHandler.channelRead is called by Netty, put your code to handle message.
You also need to read doc to know how netty encoder/decoder works.
How can I use the returned channelFuture from the callBack from other parts of my code in order to read the incoming messages?
share those ClientHandler created by NettyClient(NettyClient.java line 29)
Do I need to call again callBack, or how can I received the updated message of the channel?
if server message come,ClientHandler.channelRead is called.
Could I possible use from my code (inside a button event) something like ret.channel().read() (so as to take the last message)?
yes you could,but not a netty way,to play with netty,you write callbacks(when message come,when message sent ...),wait netty call your code,that is : the driver is netty,not you.
last,do you really need such a heavy library to do network?if not ,try This code,it simple,easy to understanding

send large message between client and server with netty

I want to send large message between client and server with netty, but when I use netty for sending large message to server, In server I cannot get message complete for first time, in server I use ChannelHandlerAdapter when send large message from client method channelReadComplete run for two seconds, it must run for first time. Please see my client code and tell me my problem.
try {
Bootstrap b = new Bootstrap();
b.group(group).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch)
throws Exception {
ChannelPipeline p = ch.pipeline();
// if (sslCtx != null) {
// p.addLast(sslCtx.newHandler(ch.alloc(), HOST,
// PORT));
// }
System.out.println("initChannel-client");
p.addLast(new DiscardClientHandler(),
new LengthFieldBasedFrameDecoder(
100 * 1024, 0, 8));
}
});
// Make the connection attempt.
ChannelFuture f = b.connect(HOST, PORT).sync();
// // Wait until the connection is closed.
// // add by test
DiscardClient discardClient = new DiscardClient();
String message = discardClient.reafFile("D:\\log\\log1.txt");
ByteBuf encoded = f.channel().alloc().buffer(message.length());
encoded.writeBytes(message.getBytes());
f.channel().write(encoded);
f.channel().flush();
f.channel().closeFuture().sync();
} finally {
// group.shutdownGracefully();
}
Best Regards

How can I forward a message that received a server to another server in Netty (Java)?

I have some clients, they communicate with one server and I need that server forward the message to another second server. Then, receive the message from the second server and send to the client.
With this method, I achieve connecting to the second server but it doesn't receive the message and throws me the following exception:
EXCEPTION: java.nio.channels.NotYetConnectedException. java.nio.channels.NotYetConnectedException
public void messageReceived(ChannelHandlerContext ctx, final MessageEvent e) throws IOException, Exception {
response = "hola" + "\r\n";
Main.creaLog("Mensaje recibido del conc: " + e.getMessage().toString());
Main.creaLog("Mensaje enviado al servidor : " + response);
ClientBootstrap bootstrap1 = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
// Configure the pipeline factory.
//bootstrap1.setPipelineFactory(new CLIENTE.ClientePipelineFactory());
bootstrap1.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
return Channels.pipeline(new ClienteHandler());
}
});
final ChannelFuture future = bootstrap1.connect(new InetSocketAddress("172.16.10.14", 12355));
Channel channel = future.getChannel();
if (channel.isWritable()) {
ChannelFuture lastWriteFuture = channel.write(e.getMessage().toString() + "\r\n");
}
close = true;
// We do not need to write a ChannelBuffer here.
// We know the encoder inserted at TelnetPipelineFactory will do the conversion.
ChannelFuture future = e.getChannel().write(response + "\r\n");
//CIERRA LA CONEXION
if (close) {
future.addListener(ChannelFutureListener.CLOSE);
}
}
I'm very thanksful if anybody can help me.
Have a look at Netty Proxy example
Right now, you are basically attempting to connect to the remote server on every message that you receive. This probably isn't what you want. You might want to connect to the remote server only once (i.e. outbound channel in Netty proxy example) and forward a new incoming message to that specific channel.

Categories