Notify another thread when ServerSocket blocks on accept() - java

I've got a pair of threads running a Socket and ServerSocket:
runThreadA() {
// Connects to B, or fails if B is not yet accepting
socket.connect();
}
runThreadB() {
// Blocks until another thread connects
serverSocket.accept();
}
Is there a way I can guarantee that B calls accept() and blocks before A calls connect()?

Check CyclicBarrier and CountDownLatch which can help you make the threads to wait each other, they might be helpful.

Code properly. If connect() fails, log the issue, wait a backoff time, and then retry, unless really too much time has passed. Even if the server is listening, connect() may fail for other reasons you and me might not be aware of (e.g. a layer somewhere in the network API has a full buffer). That's why IOException is a controlled exception, and the Java compiler expects you to explicitly handle it: IOExceptions may happen, but this is not a programming error.
private static final Logger LOG = Logger.getLogger(YourClass.class);
for (int waitTime = 10; waitTime < 10000; waitTime *= 2) {
try {
socket.connect();
} catch (ConnectException ex) {
LOG.log(Level.WARNING,
"Connection failed, retrying in " + waitTime + " ms...", ex);
Thread.sleep(waitTime);
} catch (IOException ex) {
throw new RuntimeException("Unexpected IO Exception", ex);
}
}
Even if you used some synchronization primitives, like a barrier or a signaling, the server thread would always signal before listen() actually comes into effect. So, there would still be the possibility of bad thread interleavings.

Yeap, set a reasonable timeout
runThreadA() {
Socket s = new Socket()
s.connect(endPoint, REASONABLE_TIMEOUT )
}
That way, the thread A will attempt to connect but it will wait a REASONABLE amount of time before giving up.
Update
Another approach would be to have the second thread re-try for the same amount of REASONABLE time.
runThreadA() {
maxTimeout = currentTime() + REASONABLE_TIMEOUT
while ( currentTime() < maxTimeout ) {
socket.connect()
if ( isConnected( socket ) ) {
break;
}
Thread.currentThread.sleep( A_WHILE )
}
if ( ! isConnected( socket ) ) {
.... too bad, we can't work this way
exit();
}
continue with the work here...
}

Related

How to catch a socket-timeout in Java

I'm writing a server with Java.net. Now i want to change some variables on socket-timeout.
I cant find a 'onTimeout' interface or something similar.
Now I'm searching for a solution for this problem.
You say you're using java.net so I'm assuming that you're using something like a java.net.ServerSocket. If you call setSoTimeout on your instance of ServerSocket, you will be able to set a timeout for your socket. Then, when you block during calls to accept, your ServerSocket will keep track of the timeout. As you can see in the documentation, accept throws a SocketTimeoutException if a timeout has been defined and the wait time has exceeded the timeout. So, you'll end up with something like this (forgive me for being a bit rusty on Sockets):
try(ServerSocket ssock = new ServerSocket(...))
{
ssock.setSoTimeout(10_000); // 10 second timeout
while(true)
{
Socket csock = ssock.accept();
methodToStartThreadThatHandlesClientSocket(csock);
}
}
catch(SocketTimeoutException ste)
{
//handle socket timeout
}
catch(Exception other)
{
//handle other exceptions
}

Synchronizing data output stream

I'm having a problem where I have a class that gets instantiated upon a connection to server.
The method I'm having trouble with in the class looks like so:
public void sendData(byte[] dataToSend) throws IOException {
sendLock.lock();
int dataLength = dataToSend.length;
dout.writeInt(dataLength);
dout.write(dataToSend, 0, dataLength);
dout.flush();
sendLock.unlock();
}
Where sendLock is a ReentrantLock and dout = new DataOutputStream(socket.getOutputStream());. This will work fine with a limited number of threads, but if I have a large number of threads calling this method concurrently I get a deadlock and the program just stops.
Is there any reason a deadlock would be happening here? It doesn't make sense to me, as I've removed all other locks to rule them out and I'm down to this one. Is there anyway the flush could cause things to hang or something? It just seems like at some point it never releases the lock and I'm not sure why.
If I remove the lock I get socket errors because one thread may change the dataLength before another has a chance to write, etc. But the deadlock no longer occurs.
As a reference, here's what the run method of the Receive end looks like:
public void run() {
while (socket != null) {
try {
int dataLength = din.readInt();
byte[] data = new byte[dataLength];
din.readFully(data, 0, dataLength);
Event e = ef.getEvent(data);
node.onEvent(e);
} catch (SocketException se) {
System.out.println(se.getMessage());
break;
} catch (IOException ioe) {
System.out.println(ioe.getMessage()) ;
break;
}
}
}
It's possible that one of your calls to the output stream throws an exception and sendLock.unlock() is never called. All the other threads will be left waiting forever.
Check your logs to see if one of the threads throws an exception. In your code I would use a try-catch-finally block instead of throwing IOException. This guarantees, even if something bad happens, the lock will be released so other threads can keep working.
public void sendData(byte[] dataToSend) throws IOException {
try {
sendLock.lock();
int dataLength = dataToSend.length;
dout.writeInt(dataLength);
dout.write(dataToSend, 0, dataLength);
dout.flush();
}
finally {
sendLock.unlock();
}
}

Can't detect disconnect without extra readLine() loop

I am developing a program that uses sockets and currently I have a function in my code that checks for a heartbeat from the client every second.
private void userLoop() { // checks for incoming data from client
Timer t = new Timer();
t.schedule(new TimerTask() {
#Override
public void run() {
try {
socketIn.read(); // check for heartbeat from client
String userInput;
while ((userInput = br.readLine()) != null) {
}
} catch (Exception e) {
ControlPanel.model.removeElement(getUsername());
ControlPanel.append(getUsername() + " has disconnected.");
}
}
}, 1000);
}
When a client closes the game via the X button, shutting off their computer, logging out, whatever it may be, I get the message "'username' has disconnected". This is exactly what I want, however, it only works with the while loop in the code. The while loop essentially does nothing and I have no idea why it doesn't work with out.
If I remove the while loop and I disconnect using my client nothing gets printed out server sided.
String userInput;
while ((userInput = br.readLine()) != null) {
}
The above is essentially the dead code that does nothing but without it my program doesn't work the way it should..
Why is the code needed and how can I remove it and still make my program work correctly?
In this case, your while loop is essentially stalling your program until you no longer receive an input string. It's not dead code; it is just your way of installing a wait.
Otherwise, based on my understanding in the Timer class, it only waits one second, which might be too short of a timespan for what you're waiting to capture.
I fixed my problem by changing everything in the try block with
br.readLine();
There's a saying I've heard about exception handling: "Exceptions should only be used for exceptional situations." A client disconnecting from a server is not exceptional.
Now that I have that off my chest, let's move on. According to this other question,
socket.getInputSteam.read() does not throw when I close the socket from the client
it sounds like the read call won't throw if you're closing things properly on the client side.
The problem is that when the remote socket is closed, read() does not throw an Exception, it just returns -1 to signal the end of the stream.
The following should work without needing to call readLine():
try {
int ret = socketIn.read(); // check for heartbeat from client
if (ret == -1) {
// Remote side closed gracefully
clientDisconnected();
}
} catch (SocketTimeoutException e) {
// Timeout -- handle as required
handleTimeout();
} catch (IOException e) {
// Connection lost due to I/O error
clientDisconnected()
}

How to I call a function to delay execute because of some conditions in java?

This is my run method, it opens a socket, and waiting for an accepted connection, if connection is accepted, will have a separate Thread open for execute it:
while (isKeepRun) {
socket = serverSocket.accept();
WorkTask worktask = new WorkTask();
worktask.setSocket(socket);
worktask.setIn(new ObjectInputStream(socket.getInputStream()));
worktask.setOut(new ObjectOutputStream(socket.getOutputStream()));
Thread wt = new Thread(worktask);
wt.start();
}
if (socket != null) {
socket.close();
}
if (serverSocket != null) {
serverSocket.close();
}
When the user call it to stop, they call this method, to change the while loop condition. in order to break the while loop:
public void stopWorking() {
isKeepRun = false;
}
Well, the WorkTask's run method is very simple like that:
try {
do {
objectOutputStream.flush();
receivedObj = objectInputStream.readObject();
if (receivedObj != null){
System.out.println(receivedObj.toString()+" " + receivedObj.hashCode());
}
} while (receivedObj != null
&& !receivedObj.equals(SharedConstant.SOCKET_EOF_STRING));
if (objectInputStream != null) {
objectInputStream.close();
}
if (objectOutputStream != null) {
objectOutputStream.close();
}
} catch (IOException e1) {
e1.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
The problem is assume that reading one message need 1 second per message, the user may give up to 100 messages, that's mean it requires 100 seconds to run, in the socket. When the isKeepRun is keep running, there is no problem. But when the user wanna to stop , and call stopWorking, the loop will be escaped, can the socket is closed, during the WorkTask is reading the message. How can I delay the execution of stopWorking if the socket is still reading, if the socket is finished reading, and the stopWorking will be call immediately, but if the socket don't have any thing to read, I can call the stopWorking in no delay?
Thanks.
If your worker thread is handling your client request then it should be his responsibility to close the socket connection. You should move the code that closes the accepted socket into your worker thread. Your server socket accept loop will be independent and will close as soon as the close request is made. But, the existing connections will still be valid and the worker thread can continue handling them.
There is a problem in your code:
while (isKeepRun) {
socket = serverSocket.accept();
....
}
....
if (socket != null) {
socket.close();
}
You use only one socket reference for all client socket. So if there are more than one client socket, then you will only close the last socket.
As above said, you can use wt.join() to wait for the worker thread to finish, then the main thread will finish. But you have only one reference for client socket. Even you wait, you can only wait for the last socket to finish. All the previous client socket will be closed if you set it to stop.
Try adding
wt.join()
after the wt.start(). This waits for thread to finish it's execution.
You can check if there are still active worker threads around by calling their isAlive method. To do so, you need to keep track of your workers (using some list or map).
You might also use a call back mechanism for the workers, through which they can report back, when they finished their task. Before stopping, you simply check if every worker is done.
In both cases, if there are still active workers, sleep for some time, and check again, until all threads have finished.
Edit:
Vikas Nalwar made a good point about letting the workers close the socket connection. It still might be a good idea to wait for the worker threads to finish, though.

Android - Socket time out

I have some strange socket behavior going on. I've set an timeout of 5 seconds using setSoTimeout. This should be plenty of time in my situation. According to online java documentation a SocketTimeoutException should be thrown if it times out. It also says that the socket is still valid. So I want to catch it and then continue. However instead of the inner catch, the outer catch IOException is catching the expception and when I output to the log the details it says it was a SocketTimeoutException. Another perplexing thing is I change the timeout from 5 seconds to say, 15 seconds and log the amount of time it take for every read, the times are always in the milli-second range, never even close to a second. Any ideas are GREATLY appreciated.
ReadThread code snippet
#Override
public void run()
{
try
{
while (true)
{
byte[] sizeBuffer = new byte[BYTES_FOR_MESSAGE_SIZE];
int bytesRead = this.inputStream.read(sizeBuffer);
int length = 0;
for (int i = 0; i < BYTES_FOR_MESSAGE_SIZE; i++)
{
int bitsToShift = 8 * i;
int current = ((sizeBuffer[i] & 0xff) << bitsToShift);
length = length | current;
}
byte[] messageBuffer = new byte[length];
this.socket.setSoTimeout(5000); //5 second timeout
try
{
this.inputStream.read(messageBuffer);
}
catch(java.net.SocketTimeoutException ste)
{
Log.e(this.toString(), "---- SocketTimeoutException caught ----");
Log.e(this.toString(), ste.toString());
}
}
}
catch (IOException ioe)
{
Log.e(this.toString(), "IOException caught in ReadThread");
Log.e(this.toString(), ioe.toString());
ioe.printStackTrace();
}
catch (Exception e)
{
Log.e(this.toString(), "Exception caught in ReadThread");
Log.e(this.toString(), e.toString());
e.printStackTrace();
}
this.interfaceSocket.socketClosed();
}// end run
I agree with Brian. You are probably getting the timeout on the first read, not the second. The timeout once set remains in effect until you change it again.
Your second read call where you read the 'message' seems to assume (a) that it will read the entire message and (b) that it will timeout if the entire message doesn't arrive within 5s. It doesn't work like that. It will timeout if nothing arrives within 5s, or else it will read whatever has arrived, up to message.length. But it could only be one byte.
You should use DataInputStream.readFully() to read the entire message, and you need to completely reconsider your timeout strategy.
The exception is probably caught in the first try catch because of the earlier call to this.inputStream.read(). You have two of these calls: one in the outer try, one in the inner try.
Have you validated if data is being read? If data is being read then you should expect the read operation to return after a few milliseconds. If data is not being read, then the read operation should block there for the time you specify. Maybe this has to do with the order by which you setSoTimeout (perhaps doing it earlier will help).
Good luck,
B-Rad

Categories