I am attempting to create a TCP server that accepts messages on port 5002 from an external program. However, it is not receiving messages from the external program.
@Bean
public TcpReceivingChannelAdapter inbound(AbstractServerConnectionFactory cf) {
TcpReceivingChannelAdapter adapter = new TcpReceivingChannelAdapter();
adapter.setConnectionFactory(cf);
adapter.setOutputChannel(tcpIn());
return adapter;
}
@Bean
public MessageChannel tcpIn() {
return new DirectChannel();
}
@Bean
@Transformer(inputChannel = "tcpIn", outputChannel = "serviceChannel")
public ObjectToStringTransformer transformer() {
return new ObjectToStringTransformer();
}
@ServiceActivator(inputChannel = "serviceChannel")
public void messageToService(String in) {
// Message received
}
@Bean
public AbstractServerConnectionFactory serverConnectionFactory() {
TcpNetServerConnectionFactory tcpNetServerConnectionFactory = new TcpNetServerConnectionFactory(5002);
tcpNetServerConnectionFactory.setSoTimeout(5000);
tcpNetServerConnectionFactory.setMapper(new TimeoutMapper());
return tcpNetServerConnectionFactory;
}
To verify my TCP server is working I used telnet like so, and the program did get the text "hello".
telnet 192.168.1.2 5002
Trying 192.168.1.2...
Connected to 192.168.1.2.
Escape character is '^]'.
hello
Setting up wireshark I can see that the computer is receiving messages from the external program (which I am expecting) on port 5002. Why is my program not able to receive these messages?
Update on final solution:
Since the payload did not have a stop line, I had to implement my own deserializer as described by @Artem Bilan. I used the '~' character to signal an end of line from the client.
@Bean
public AbstractServerConnectionFactory serverConnectionFactory() {
TcpNetServerConnectionFactory tcpNetServerConnectionFactory = new TcpNetServerConnectionFactory(tcpPort);
tcpNetServerConnectionFactory.setSoTimeout(0);
tcpNetServerConnectionFactory.setDeserializer(endOfLineSerializer());
tcpNetServerConnectionFactory.setSerializer(endOfLineSerializer());
tcpNetServerConnectionFactory.setMapper(new TimeoutMapper());
return tcpNetServerConnectionFactory;
}
Sample serializer that I implemented:
public class EndOfLineSerializer extends AbstractPooledBufferByteArraySerializer {
private static final char MANUAL_STOP_LINE = '~';
private static final char AUTO_STOP_LINE = '\t';
private static final byte[] CRLF = "\r\n".getBytes();
/**
* Reads the data in the inputStream to a byte[]. Data must be terminated
* by a single byte. Throws a {@link SoftEndOfStreamException} if the stream
* is closed immediately after the terminator (i.e. no data is in the process of
* being read).
*/
@Override
protected byte[] doDeserialize(InputStream inputStream, byte[] buffer) throws IOException {
int n = 0;
int bite;
try {
while (true) {
try {
bite = inputStream.read();
} catch (SocketTimeoutException e) {
bite = -1;
}
if (bite < 0) {
// Payload complete
break;
}
if ((n > 0 && bite == '\n' && buffer[n - 1] == '\r') || bite == this.MANUAL_STOP_LINE || bite == this.AUTO_STOP_LINE) {
break;
}
buffer[n++] = (byte) bite;
if (n >= this.maxMessageSize) {
throw new IOException("Terminator not found before max message length: " + this.maxMessageSize);
}
}
return copyToSizedArray(buffer, n);
} catch (IOException e) {
publishEvent(e, buffer, n);
throw e;
} catch (RuntimeException e) {
publishEvent(e, buffer, n);
throw e;
}
}
/**
* Writes the byte[] to the stream and appends the CRLF.
*/
@Override
public void serialize(byte[] bytes, OutputStream outputStream) throws IOException {
outputStream.write(bytes);
outputStream.write(this.CRLF);
}
}
The TcpNetServerConnectionFactory
uses ByteArrayCrLfSerializer
by default, where this is what is count as a message delimiter:
private static final byte[] CRLF = "\r\n".getBytes();
So, you should be sure that your client send message with the proper symbol in the end.
There are a bunch of out-of-the-box serializers for your choice:
Or you can implement your own Deserializer
and inject into the serverConnectionFactory
bean definition.