Soquetes Java e Conexões Derrubadas

Qual é a maneira mais apropriada para detectar se um soquete foi descartado ou não? Ou se um pacote foi realmente enviado?

Eu tenho uma biblioteca para enviar o Apple Push Notifications para iPhones através dos Apple Gatways (disponível no GitHub). Os clientes precisam abrir um soquete e enviar uma representação binária de cada mensagem; mas infelizmente a Apple não retorna nenhum reconhecimento. A conexão pode ser reutilizada para enviar várias mensagens também. Estou usando as conexões simples do Java Socket. O código relevante é:

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);

Em alguns casos, se uma conexão for interrompida enquanto uma mensagem é enviada ou antes;Socket.getOutputStream().write() termina com sucesso embora. Espero que seja devido a janela TCP não está esgotada ainda.

Existe uma maneira que eu possa dizer com certeza se um pacote realmente entrou na rede ou não? Eu experimentei com as duas soluções a seguir:

Insira um adicionalsocket.getInputStream().read() operação com um tempo limite de 250 ms. Isso força uma operação de leitura que falha quando a conexão é interrompida, mas trava de outra forma por 250ms.

definir o tamanho do buffer de envio de TCP (por ex.Socket.setSendBufferSize()) para o tamanho binário da mensagem.

Ambos os métodos funcionam, mas degradam significativamente a qualidade do serviço; O throughput vai de 100 mensagens / segundo a cerca de 10 mensagens / segundo no máximo.

Alguma sugestão?

ATUALIZAR:

Desafiado por múltiplas respostas questionando a possibilidade do descrito. Eu construí testes de "unidade" do comportamento que estou descrevendo. Confira os casos da unidade emEssência 273786.

Ambos os testes de unidade possuem dois encadeamentos, um servidor e um cliente. O servidor fecha enquanto o cliente está enviando dados sem uma IOException lançada de qualquer maneira. Aqui está o método principal:

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");
}

[A propósito, eu também tenho uma biblioteca de testes de concorrência, que torna a configuração um pouco melhor e mais clara. Confira também a amostra na essência].

Quando executado, recebo a seguinte saída:

Closed socket
writing new packets
Finished writing
Run without any errors

questionAnswers(3)

yourAnswerToTheQuestion