views:

612

answers:

8

I have a server and has 2 clients connecting to it through TCP. These clients continuously send information to the server. This is a proxy server and relays the messages it receives from the client. But it has to do relay the messages alternately. i.e. message from client A, then message from client B and again A then B and so on. This I can achieve by checking where the message is coming from and then relay messages alternately and ignoring consecutive messages from the same client.

However I also do not want the server to get bogged down if any of the clients disconnects or is not sending messages. If this happens the proxy will continue to wait forever for the some message from the client which is now disconnected (or for some reason not sending message). In this case I would want the server to relay the message from from sole connected client.

Instead I am thinking if something like this is possible. If I get 2 consecutive messages from the same client I would like to check if the other client is read to send me a message. My question is whether it is possible to check from the other client's socket if there is a message buffered and ready to be sent. In this this case can ignore the consecutive message from the same the client and instead first send the message from the other client. (that I have checked.)

Is this possible? Hope I have asked the question clearly.

Thanks

+1  A: 

You could set the read timeout on the sockets to be something short (like a second maybe or whatever time you want to wait for each client) with the setSoTimeout() method; that way, when you get the second message from client A, you can just read() from client B's socket and if you get a SocketTimeoutException then you can process A's message.

But you will have to modify your current code to catch any SocketTimeoutExceptions you may get whenever you're reading from the sockets.

Chochos
A: 

For reasons I'll explain below, I would just treat the interesting event as being whether or not the message-- or part of a message-- has arrived to your server. So:

  • have a thread reading data from each client, as you've probably got
  • have another thread that does the message collation
  • have the client threads signal to the message collation thread when: the very first part of a new message arrives; when a message is complete from a client (at that point, it passes the actual message to the collation thread)
  • then, if your collation thread hasn't had the initial signal that the "first part of a message has arrived", just treat that as "the client isn't ready to send a message".

You probably want to set a timeout on the sockets as another poster has suggested, although with this threading model, I don't think it's crucial-- it really depends on what policy you want to impose.

Your other suggestion of asking the client is sort of possible, but it's probably not the solution you really want. You open another channel to the client to ask "have you just sent some data". But if the client has, and you have't got it, then in general Something Has Just Gone Wrong and it's not clear whether that knowledge will help you much. Or it might say "no" and then just at that moment send data. And will the other channel to the client work? If the client says it's sent something, how do you know whether it's just about to arrive or has "got lost" and will never arrive...?

Neil Coffey
+1  A: 

The problem is this: To test if a client is dropped you must do a read on the socket, however, Java's Socket class does not allow for asynchronous reading on a socket. Therefore if you do a read without a timeout set, and there is nothing being sent, the application will hold the process hostage waiting for something to be read.

So, after you instantiated your socket you need to use: Socket.setSoTimeout(int) to give it a period of time to "wait" before it times out. What is going to happen is that you will time out everytime you attempt to read, and if the read comes back -1 you know the client has disconnected.

    Socket clientSocket=theServerSocket.accept();
    clientSocket.setSoTimeout(1);
    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));

while(serverIsRunning){
       try{
           if(bufferedReader.read()==-1){
                  logger.info("The server has disconnected!");
                  //Do whatever clean up you need, ect
           }
       }
       catch (SocketTimeoutException e){
           //the server is still running, yay?!
       }
}
windfinder
A: 

The way I am thinking of doing this is: If I get 2 consecutive messages from client A, I will get the inputStream of socket connected to client B. Then call the available() method on the inputStream and see if there are any bytes available to read. If there are bytes to read then read from the socket of client B. Else relay the message from from client A (the second consecutive message).

Any comments on this.

So, both client A and client B should be sending the same message at the same time? You are just trying to make one a backup server?
windfinder
Actually they are sending different messages and I would like the server to relay messages alternately. However if one client for some reason delays sending messages, I don't want the other client's messages to be blocked from being relayed.
A: 

If client A sends a second message before client B sends a message, do you print out the second message from client A? In that case, it's not really alternating at all, you're just printing whichever messages arrive in the order they arrive. Or, do you want to wait a bit to see if a message arrives from client B?

I think either way, you'll want a separate thread for each client, running in addition to the "main" thread. Each client thread constantly tries to read a message from its respective client, which blocks until it receives a message. When a socket thread receives a message, it puts it onto a SynchronousQueue, of which there is one instance for all three threads. This put operation will block until the main thread calls take on the SynchronousQueue.

The main thread can simply loop and call take on the SynchronousQueue. This will block until one of the client threads calls put.

If you want to do alternating messages, you could have two SynchronousQueues instead, one for each client. You'd want to introduce some delay in your main loop, to give both clients a chance to write their messages before forwarding it on.

Sam Barnum
+2  A: 

I am reading the problem as thus:

You have:

1 server

2 clients

Your server gets messages from client 1 and 2 and forwards them.

The clients are different producers, meaning the messages they are sending could potentially be different. What you want is the messages from the clients to be sent alteratively out from your server, but not to "wait" if a client has dropped.

In this scenario, I suggest that you have two queues (client1queue and client2queue) in your server.

You will have to read from the sockets in two seperate threads, and when a message comes in add it to its corresponding queue. client1Socket -> client1queue client2Socket -> client2queue

Then, in a third thread, have the server forward the messages, alternating pulling these messages from client1queue and client2queue.

To solve your problem of "not waiting" if the queue is empty simply skip that queues' "turn." This insures sending all messages at the fastest possible rate while still getting all the messages across. The downside is that it only alternates if a message is ready to be sent. You could, of course, have it wait for X amount to see if a message comes for the other queue, but I don't see why you would want to if the system is supposed to work regardless of the client state.

windfinder
A: 

I would look up the javadoc for the available() method. It doesn't do what you think it does.

Dan.StackOverflow
A: 

You have 2 separate problems:

1) Read messages from several clients, without blocking.

2) Writing messages to the output in a fair way.

The first problem can be solved if you use a separate thread for each client socket. If you have many clients, you might consider using nonblocking io using SocketChannel and and a Selector.

Remember that TCP/IP streams might deliver several or only partial messages in each read operation, depending on the network and socket options, so you have to take care of that.

The second problem can be solved with keeping a queue for each client or perhaps using a priority queue and attach a priority to each message depending on the clients activty.

KarlP