Note that there are some explanatory texts on larger screens.

plurals
  1. POJava Sockets and Dropped Connections
    primarykey
    data
    text
    <p>What's the most appropriate way to detect if a socket has been dropped or not? Or whether a packet did actually get sent?</p> <p>I have a library for sending Apple Push Notifications to iPhones through the Apple gatways (<a href="http://github.com/notnoop/java-apns" rel="noreferrer">available on GitHub</a>). Clients need to open a socket and send a binary representation of each message; but unfortunately Apple doesn't return any acknowledgement whatsoever. The connection can be reused to send multiple messages as well. I'm using the simple Java Socket connections. The relevant code is:</p> <pre><code>Socket socket = socket(); // returns an reused open socket, or a new one socket.getOutputStream().write(m.marshall()); socket.getOutputStream().flush(); logger.debug("Message \"{}\" sent", m); </code></pre> <p>In some cases, if a connection is dropped while a message is sent or right before; <code>Socket.getOutputStream().write()</code> finishes successfully though. I expect it's due to the TCP window isn't exhausted yet.</p> <p>Is there a way that I can tell for sure whether a packet actually got in the network or not? I experimented with the following two solutions:</p> <ol> <li><p>Insert an additional <code>socket.getInputStream().read()</code> operation with a 250ms timeout. This forces a read operation that fails when the connection was dropped, but hangs otherwise for 250ms.</p></li> <li><p>set the TCP sending buffer size (e.g. <code>Socket.setSendBufferSize()</code>) to the message binary size.</p></li> </ol> <p>Both of the methods work, but they significantly degrade the quality of the service; throughput goes from a 100 messages/second to about 10 messages/second at most.</p> <p>Any suggestions?</p> <p>UPDATE:</p> <p>Challenged by multiple answers questioning the possibility of the described. I constructed "unit" tests of the behavior I'm describing. Check out the unit cases at <a href="http://gist.github.com/273786" rel="noreferrer">Gist 273786</a>.</p> <p>Both unit tests have two threads, a server and a client. The server closes while the client is sending data without an IOException thrown anyway. Here is the main method:</p> <pre><code>public static void main(String[] args) throws Throwable { final int PORT = 8005; final int FIRST_BUF_SIZE = 5; final Throwable[] errors = new Throwable[1]; final Semaphore serverClosing = new Semaphore(0); final Semaphore messageFlushed = new Semaphore(0); class ServerThread extends Thread { public void run() { try { ServerSocket ssocket = new ServerSocket(PORT); Socket socket = ssocket.accept(); InputStream s = socket.getInputStream(); s.read(new byte[FIRST_BUF_SIZE]); messageFlushed.acquire(); socket.close(); ssocket.close(); System.out.println("Closed socket"); serverClosing.release(); } catch (Throwable e) { errors[0] = e; } } } class ClientThread extends Thread { public void run() { try { Socket socket = new Socket("localhost", PORT); OutputStream st = socket.getOutputStream(); st.write(new byte[FIRST_BUF_SIZE]); st.flush(); messageFlushed.release(); serverClosing.acquire(1); System.out.println("writing new packets"); // sending more packets while server already // closed connection st.write(32); st.flush(); st.close(); System.out.println("Sent"); } catch (Throwable e) { errors[0] = e; } } } Thread thread1 = new ServerThread(); Thread thread2 = new ClientThread(); thread1.start(); thread2.start(); thread1.join(); thread2.join(); if (errors[0] != null) throw errors[0]; System.out.println("Run without any errors"); } </code></pre> <p>[Incidentally, I also have a concurrency testing library, that makes the setup a bit better and clearer. Checkout the sample at gist as well].</p> <p>When run I get the following output:</p> <pre><code>Closed socket writing new packets Finished writing Run without any errors </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload