I was using
TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port);
for TcpOutboundGateway, normally TcpOutboundGateway is working req/reply order but in my case I extend TcpOutboundGateway to receive arbitrary messages with MessageChannel. This is why ı thought that i should use
cf.setLeaveOpen(true)
to keep connection open.
Although i started to use that option, after long time when i called tcp server again i have received
Exception like
org.springframework.integration.MessageTimeoutException: Timed out waiting for response
but i did not understand why i am taking this error because i set "true" to keep connection open in my connection factory.
THEN
I did some google and it was supposed to use CachingClientConnectionFactory, I understand that it is by default single-use=true and not supposed to change it false, but then i assume that connection will be open and close in my each request response transaction so is it obstacle to receive arbitrary data from server without any request from client ?
OR
How should i keep open connection between client and server ? should i use
cf.setSoKeepAlive(true) ?
to keep connection open ?
Are
cf.setSoKeepAlive(true) and cf.setLeaveOpen(true)
same with each other ?
EDIT
Also when i use cf.setSoKeepAlive(true), after 1 hour i got same exception too.
Full code :
private MessageChannel createNewSubflow(Message<?> message) {
String host = (String) message.getHeaders().get("host");
Integer port = (Integer) message.getHeaders().get("port");
boolean hasThisConnectionIrregularChannel = message.getHeaders().containsKey("irregularMessageChannelName");
Assert.state(host != null && port != null, "host and/or port header missing");
String flowRegisterKey;
if (hasThisConnectionIrregularChannel) {
flowRegisterKey = host + port + ".extended";
} else {
flowRegisterKey = host + port;
}
TcpNetClientConnectionFactory cf = new TcpNetClientConnectionFactory(host, port);
CachingClientConnectionFactory ccf = new CachingClientConnectionFactory(cf, 20);
ccf.setSoKeepAlive(true);
ByteArrayCrLfSerializer byteArrayCrLfSerializer = new ByteArrayCrLfSerializer();
byteArrayCrLfSerializer.setMaxMessageSize(1048576);
ccf.setSerializer(byteArrayCrLfSerializer);
ccf.setDeserializer(byteArrayCrLfSerializer);
TcpOutboundGateway tcpOutboundGateway;
if (hasThisConnectionIrregularChannel) {
String unsolicitedMessageChannelName = (String) message.getHeaders().get("irregularMessageChannelName");
DirectChannel directChannel = getBeanFactory().getBean(unsolicitedMessageChannelName, DirectChannel.class);
tcpOutboundGateway = new ExtendedTcpOutboundGateway(directChannel);
} else {
tcpOutboundGateway = new TcpOutboundGateway();
}
tcpOutboundGateway.setRemoteTimeout(20000);
tcpOutboundGateway.setConnectionFactory(ccf);
IntegrationFlow flow = f -> f.handle(tcpOutboundGateway);
IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
this.flowContext.registration(flow)
.addBean(ccf)
.id(flowRegisterKey + ".flow")
.register();
MessageChannel inputChannel = flowRegistration.getInputChannel();
this.subFlows.put(flowRegisterKey, inputChannel);
return inputChannel;
}
Why are you using the CachingClientConnectionFactory? It is not needed when you keep the connection open; it is intended to be used when you want to maintain multiple open connections.
Timed out waiting for response
Means the socket was open just fine (from the client's perspective) when you sent the request; we just didn't get a reply. This could mean that some network component (router) silently closed the socket due to inactivity. Keep-alives should help with that but it depends on your operating system and how often the TCP stack is configured to send keep alives.
Related
I have a software driver which communicates with a third-party controller; I have an API for using the latter but no visibility of its source code, and the supplier is not co-operative in trying to improve things!
The situation is as follows.
To send a request to the controller, I send an XML packet as the content of an HTTP POST to a servlet, which then sends me the response. The original code, implemented by a previous developer, works stably using java.net.Socket. However, our driver is implemented such that a new socket is created for EVERY request sent and, if the driver gets busy, the third-party controller struggles to keep up in terms of socket handling. In fact, their support guy said to me: "You really need to leave 5 seconds between each request...". This simply isn't commercially acceptable.
To improve performance, I wanted to try leaving our end of the socket open and reusing the socket pretty much indefinitely (given that connections can drop unexpectedly of course, but that's the least of my concerns and is manageable). However, whatever I seem to do, the effect is that if I use Comms.getSocket(false), a new socket is created for each request and everything works OK but bottlenecks when busy. If I use Comms.getSocket(true), the following happens:
Controller is sent first request
Controller responds to first request
Controller is sent second request (maybe 5 seconds later)
Controller never responds to second request or anything after it
postRequest() keeps getting called: for the first 12 seconds, the console outputs "Input shut down ? false" but, after that, the code no longer reaches there and doesn't get past the bw.write() and bw.flush() calls.
The controller allows both HTTP 1.0 and 1.1 but their docs say zilch about keep-alive. I've tried both and the code below shows that I've added Keep-Alive headers as well but the controller, as server, I'm guessing is ignoring them -- I don't think I have any way of knowing, do I ? When in HTTP 1.0 mode, the controller certainly returns a "Connection: close" but doesn't do that in HTTP 1.1 mode.
The likelihood is then that the server side is insisting on a "one socket per request" approach.
However, I wondered if I might be doing anything wrong (or missing something) in the following code to achieve what I want:
private String postRequest() throws IOException {
String resp = null;
String logMsg;
StringBuilder sb = new StringBuilder();
StringBuilder sbWrite = new StringBuilder();
Comms comms = getComms();
Socket socket = comms.getSocket(true);
BufferedReader br = comms.getReader();
BufferedWriter bw = comms.getWriter();
if (null != socket) {
System.out.println("Socket closed ? " + socket.isClosed());
System.out.println("Socket bound ? " + socket.isBound());
System.out.println("Socket connected ? " + socket.isConnected());
// Write the request
sbWrite
.append("POST /servlet/receiverServlet HTTP/1.1\r\n")
.append("Host: 192.168.200.100\r\n")
.append("Connection: Keep-Alive\r\n")
.append("Keep-Alive: timeout=10\r\n")
.append("Content-Type: text/xml\r\n")
.append("Content-Length: " + requestString.length() + "\r\n\r\n")
.append(requestString);
System.out.println("Writing:\n" + sbWrite.toString());
bw.write(sbWrite.toString());
bw.flush();
// Read the response
System.out.println("Input shut down ? " + socket.isInputShutdown());
String line;
boolean flag = false;
while ((line = br.readLine()) != null) {
System.out.println("Line: <" + line + ">");
if (flag) sb.append(line);
if (line.isEmpty()) flag = true;
}
resp = sb.toString();
}
else {
System.out.println("Socket not available");
}
return resp; // Another method will parse the response
}
To ease testing, I provide the socket using an extra Comms helper class and a method called getSocket(boolean reuse) where I can choose to always create a new socket or reuse the one that Comms creates for me, as follows:
public Comms(String ip, int port) {
this.ip = ip;
this.port = port;
initSocket();
}
private void initSocket() {
try {
socket = new Socket(ip, port);
socket.setKeepAlive(true);
socket.setPerformancePreferences(1, 0, 0);
socket.setReuseAddress(true);
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
br = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
System.out.println("### CREATED NEW SOCKET");
}
catch (UnknownHostException uhe) {
System.out.println("### UNKNOWN HOST FOR SOCKET");
}
catch (IOException ioe) {
System.out.println("### SOCKET I/O EXCEPTION");
}
}
public BufferedReader getReader() { return br; }
public BufferedWriter getWriter() { return bw; }
public Socket getSocket(boolean reuse) {
if (! reuse) initSocket();
return socket;
}
Can anyone help ?
If we assume that keep-alive thing is working as expected, I think the line while ((line = br.readLine()) != null) is a faulty one, as this is kind of infinity loop.
readline() returns null when there is no more data to read, e.g. a EOF, or when server/client closes the connection, that will break-down your reusing socket solution, since an open stream will never cause a null to a readLine() call, but blocking.
You need to fix the alg about reading a response (why not using implemented http client?), checking content-length, and when read the amount of required data from body, go for next loop by keeping the socket alive.
After that setting flag to true, you have to know what kind of data should be read(considering mime/content-type), besides that, the length of data, so reading data using readLine() may not be a good practice here.
Also make sure server allow for persistence connection, by checking if it respects it by responsing the same connection:keep-alive header.
I am being asked to design the following programming problem.
Previously when a client sent a request to my application I was required to route it towards a specific port on another server. The server would response in a timely manner and a response would be sent from their server to my server and I would send the information to my client. This worked fine until the load increased and we realized that the other server did not handle multi threaded calls.
I have been given a limited set of ports by the other app so that whenever a load of client requests enters my application I must balance the load among those ports in such a manner that if port A is not free I send the client request to port B and if B is not free it goes to port C. [I know that this is not a correct solution, management wants it to work that way]
All calls handled are synchronous and all ports (requirement from application X) must always be kept open.
My biggest problems at the moment are:
Knowing when a port is free. (not waiting for a response)
How to push the load to the other ports.
I need some pointers to where to head to.
Could you please help?
What I have done so far is load multiple sockets into an array of SocketAddress.
Selector socketSelector = SelectorProvider.provider().openSelector();
int interestSet = SelectionKey.OP_CONNECT | SelectionKey.OP_WRITE | SelectionKey.OP_READ;
List<SocketAddress> socketAddresses = FactorySockectConnection.getSocketInstances();
for (SocketAddress socketAddress : socketAddresses) {
SocketChannel socket = SocketChannel.open(socketAddress);
socket.configureBlocking(false);
socket.register(socketSelector, interestSet);
}
System.out.println("Start");
while (true) {
try {
socketSelector.select();
Set<SelectionKey> keys = socketSelector.selectedKeys();
Iterator<SelectionKey> keyIterator = keys.iterator();
while (keyIterator.hasNext()) {
SelectionKey selectedKey = keyIterator.next();
if (selectedKey.isConnectable()) {
SocketChannel connectChannel = (SocketChannel) selectedKey.channel();
connectChannel.finishConnect();
}
if (selectedKey.isWritable() && requestMessageByte != null) {
SocketChannel writeChannel = (SocketChannel) selectedKey.channel();
ByteBuffer buf = ByteBuffer.wrap(requestMessageByte.getBytes());
while (buf.hasRemaining()) {
writeChannel.write(buf);
}
requestMessageByte = null;
}
if (selectedKey.isReadable() && responseMessage == null) {
SocketChannel readChannel = (SocketChannel) selectedKey.channel();
ByteBuffer readBuffer = ByteBuffer.allocate(1024);
readChannel.read(readBuffer);
responseMessage = new String(readBuffer.array());
}
keyIterator.remove();
}
I am planning on using a load balancer. I will keep you posted.
I made a python "queue" (similar to a JMS protocol) that will receive questions from two Java clients. The python-server will receive the message from one of the Java clients and the second one will read the question and post an answer. The connection and messaging works, the problem comes when a Java client answers with a String of great length.
The response received by python is incomplete! What is worse, the message is cut at a certain number of characters and always at the same length, but, that number is different if someone else hosts the server. (i.e.: friend1 hosts the server, friend2 sends response, length received: 1380chars. Friend2 hosts the server, friend1 posts the answer, length received: 1431chars) This is the server-side python code:
s = socket.socket()
host = socket.gethostname()
# host = "192.168.0.20"
port = 12345
s.bind((host, port))
s.listen(5)
while True:
c, addr = s.accept()
# print 'Got connection from', addr
message = c.recv(8192) #Is this length a problem?
# print message
message = message.strip()
ipAddress = addr[0]
I read questions here on StackOverflow, that c.recv() should have no problem with a big number of bytes and our response is somewhere close to 1500 characters. This is the java client:
private void openConnection(){
try {
socket = new Socket(HOST, PORT);
out = new PrintWriter(socket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(
socketPregunta.getInputStream()));
stdIn = new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e) {}
}
public void sendAnswer(String answer) throws IOException{
openConnection();
out.write("PUBLISH-" + answer); //This answer is send incomplete!
out.flush();
closeConnection();
}
Thanks in advance!
From the documentation:
recv(buffersize[, flags]) -> data
Receive up to buffersize bytes from the socket. For the optional
flags argument, see the Unix manual. When no data is available, block
until at least one byte is available or until the remote end is
closed. When the remote end is closed and all data is read, return
the empty string.
So recv() can return fewer bytes than you ask for, which is what's happening in your case. There is discussion of this in the socket howto.
Basically you need to keep calling recv() until you have received a complete message, or the remote peer has closed the connection (signalled by recv() returning an empty string). How you do that depends on your protocol. The options are:
use fixed sized messages
have some kind of delimiter or sentinel to detect end of message
have the client provide the message length as part of the message
have the client close the connection when it has finished sending a message. Obviously it will not be able to receive a response in this case.
Looking at your Java code, option 4 might work for you because it is sending a message and then closing the connection. This code should work:
s = socket.socket()
host = socket.gethostname()
# host = "192.168.0.20"
port = 12345
s.bind((host, port))
s.listen(5)
while True:
c, addr = s.accept()
# print 'Got connection from', addr
message = []
chars_remaining = 8192
recv_buf = c.recv(chars_remaining)
while recv_buf:
message.append(recv_buf)
chars_remaining -= len(recv_buf)
if chars_remaining = 0:
print("Exhausted buffer")
break
recv_buf = c.recv(chars_remaining)
# print message
message = ''.join(message).strip()
ipAddress = addr[0]
I am doing doing multi threaded client-server architecture where the server will spawn a new thread every time a client connects.
The protocol of my communication is as follows
When Client connect to Server, a connection is setup. Every time the Client clicks the login button, the username and password will be sent from Client to Server.
Eg :
String username=Jtextfield[0].getText();
char[] password_char = Jpasswordfield.getPassword();
//convert from char array to String
String password_str = new String(password_char);
//hash player password
String playerhashpassword = Utility.getHash(password_str);
//send to server for login validation
Utility.WriteToServer(username);
Utility.WriteToServer(playerhashpassword);
If its a invalid login, the server will reply "No". The login validation is done like this
while (loginsuccess == false)
{
//if server sent something
//then read
playername = ReadFromClient();
playerhashpassword = ReadFromClient();
loginsuccess = gm.loginsuccess(playername, playerhashpassword);
}
The problem with this snippet of code is that it will keep reading what the Client send though the Client has not send anything resulting in a NullPointerException.
How do I ReadFromClient only when the Client has send something ???
ReadFromClient
public String ReadFromClient()
{
String inputLine;
try
{
inputLine=in.readLine();
return inputLine;
}
catch(IOException e)
{
System.out.println("Read or write to socket failed.");
System.exit(2);
}
return "willneverreachhere";
}
You have overlooked that readLine() can return null. You need to check it.
I am writing a proxy server in Java.
Initially, I do (simplified)
server = new ServerSocket(5568);
incoming = server.accept();
input = incoming.getInputStream();
...
outgoing = new Socket(host, 80);
output = outgoing.getOutputStream();
output.write(inputbuffer, 0, i);
where inputbuffer is some collection of bytes received so far (I read the incoming data up until the part where I know the host header, and then open a connection to the server and send what I have so far). So server is my welcome socket, input is the data coming to my proxy from the client, and output is the data to the serve from my proxy.
Next, I want the output from the server to be written to the client in parallel with the client still possibly writing stuff to the server. So I create a separate thread to read from the client:
final InputStream finalInput = input;
final OutputStream finalOutput = output;
Thread sendingStuff = new Thread(){
public void run(){
int c;
while ((c = finalInput.read()) != -1){
finalOutput.write((byte)c);
finalOutput.flush();
}
finalInput.close();
finalOutput.close();
}
}
sendingStuff.start();
Finally, I have a different section in the main thread to read from the server and write that to the client.
InputStream reverseInput = outgoing.getInputStream();
OutputStream reverseOutput = incoming.getOutputStream();
int c;
while ((c = reverseInput.read()) != -1){
reverseOutput.write((byte)c);
reverseOutput.flush();
}
reverseInput.close();
reverseOutput.close();
What happens is I get input, and send output, but the browser spins forever and the line in the thread that's reading from the client never gets a -1 signal.
A lot of the time I get errors that say things like "invalid header name" or "your browser sent a request that the server could not understand" and I think it has to do with this problem I'm having. One time I even got an IOException: Socket Closed on the line that reads from the client.
So why isn't the client sending an EOF? And is this the right way to go about doing this?
"I think it's because my HTTP request has Connection: keep-alive. How do I handle this?"
I think maybe you can just open your socket once for one connection.
Try to have flag like isNewConnection. set it to true at first and after the connection is initiated, set it to false.