Сокет закрыт до возможности чтения из ObjectInputStream(BufferedInputStream(Socket.getInputStream))

Я написал небольшую клиент-серверную программу, которая уже работала один раз, но после добавления к ней потоков и некоторых реальных входных данных я всегда получаю закрытый сокет, прежде чем смогу прочитать объект (строку). Программа всегда печатает "Клиент уже закрыл соединение!" из функции handleConnection в ProcessDataThread.

Код клиента:

synchronized private static void sendToServer(){
        Socket clientSocket = null;
    BufferedOutputStream socketOut = null;
        ObjectOutputStream out = null;
    try{ 

        String xmlToSend = "<startTag>\n<someOtherTag id=\"5555\">\n12345\n</someOtherTag>\n</startTag>\n";

        Log.d(TAG, "Trying to send the following to the Server:" + xmlToSend);

        //TODO load these from file
        clientSocket = new Socket( "10.0.2.2", 7777);
        socketOut = new BufferedOutputStream(clientSocket.getOutputStream());
        out = new ObjectOutputStream(socketOut);
        out.writeObject(xmlToSend);
        out.flush();

    }catch(Exception ex){
        Log.e(TAG, "Could not write File to Server.", ex);
    }
    finally{
        try{
            if(clientSocket != null){
                clientSocket.close();
            }
            if(out != null){
                out.close();
            }
        }catch(IOException ex){
            Log.e(TAG, "Could not close Socket.");
        }
    }
}

Код сервера:

Поток-получатель:

public void run()
{
    try {
        ServerSocket server = new ServerSocket(port);
        //Only block for 10 Seconds and try again
        server.setSoTimeout(10000);
        while(!server.isClosed() && !stopped){
            //Run
             Socket client = null;
              try
              {
                client = server.accept();
                System.out.println("Accepted ClientConnection from " + client.getRemoteSocketAddress());
                new ProcessDataThread(client).start();
              }
              catch( SocketTimeoutException tx){
                  //nothing
              }
              catch ( IOException e ) {
                e.printStackTrace();
              }
              finally {
                if ( client != null )
                  try { client.close(); } catch ( IOException e ) { e.printStackTrace(); }
              }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

}

ProcessDataThread:

public class ProcessDataThread extends Thread {

Socket client;

public ProcessDataThread(Socket sock) {
    // xmlToProcess = xmlString;
    this.client = sock;
}

private String handleConnection() {

    BufferedInputStream socketIn = null;
    ObjectInputStream in = null;
    String xmlToProcess = null;
    try {
        if(!client.isClosed()){
            System.out.println("Trying to read from Stream;");
            socketIn = new BufferedInputStream(client.getInputStream());
            in = new ObjectInputStream(socketIn);

            Object xmlString = in.readObject();
            System.out.println("Read some Object from Stream:" + xmlString.toString());
            if (xmlString instanceof String) {
                xmlToProcess = (String) xmlString;
                System.out.println("Received the following XML:\n" + xmlToProcess);
            }
        }else{
            System.out.println("Client has already closed Connection!");
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (EOFException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (socketIn != null) {
                socketIn.close();
            }
            if(client != null){
                client.close();
            }
        } catch (IOException ioex) {
            ioex.printStackTrace();
        }
    }
    return xmlToProcess;
}

@Override
public void run() {

    String xmlToProcess = handleConnection();

    if (xmlToProcess == null || xmlToProcess.isEmpty()) {
        // Es konnte kein String vom Client gelesen werden.
        return;
    }

            System.out.println(xmlToProcess);
 }
}

Я внес некоторые изменения в предложения jboi. Это то, что я получил сейчас. Ошибка остается прежней. Я даже не могу прочитать поток на сервере, потому что client.getClosed() всегда верно!

В клиентском коде:

clientSocket = new Socket( "10.0.2.2", 7777);
clientSocket.setTcpNoDelay(true);
socketOut = new BufferedOutputStream(clientSocket.getOutputStream());
out = new ObjectOutputStream(socketOut);
out.writeObject(xmlToSend);
out.flush();
socketOut.flush();
//Close Output on Socket to signalize the Server that we finished writing!
clientSocket.shutdownOutput();

in = clientSocket.getInputStream();
byte[] receivedData = new byte[8192];
while(in.read(receivedData) != -1) {
    //Wait for the Server to Close the Connection
}

В коде сервера

socketIn = new BufferedInputStream(client.getInputStream());
in = new ObjectInputStream(socketIn);
Object xmlString = in.readObject();
System.out.println("Read some Object from Stream:" + xmlString.toString());
if (xmlString instanceof String) {
    xmlToProcess = (String) xmlString;
System.out.println("Received the following XML:\n" + xmlToProcess);
}

out = client.getOutputStream();
out.write(1);
//Signalize the Client that we have read everything
client.shutdownOutput();

person asgaroth    schedule 29.08.2013    source источник


Ответы (4)


Весьма вероятно, что ваш клиент закрыл сокет в блоке finally до того, как сервер смог прочитать данные.

В вашем окончательном блоке клиентов вы должны использовать socket.shutdownOutput, затем прочитать на клиенте все входящие данные до EOF, а затем закрыть сокет.

На вашем сервере вы читаете до EOF, а затем отправляете объект как подтверждение, например. Количество байтов в сообщении. Вы также заканчиваете отправку с помощью socket.shutdownOutput(), как вы это делали на клиенте. Это снова помещает EOF в конец данных. Этот EOF принимается клиентом и, наконец, закрывает сокет.

person jboi    schedule 29.08.2013
comment
Спасибо за ваш ответ! Можете ли вы привести пример того, как читать до EOF? Должен ли я создавать InputStream в клиенте? Потому что обычно я бы только отправил и ничего не получил бы. - person asgaroth; 29.08.2013
comment
Я думаю, что ваш ответ может быть правильным. Я просто не могу понять, как это сделать правильно. Я обновил свой вопрос к моему тесту с вашими предложениями, но он все еще не работает. @jboi - person asgaroth; 29.08.2013
comment
Я принял ваш ответ, потому что это хороший способ проверить, все ли готово. - person asgaroth; 29.08.2013
comment
@asgaroth Мне жаль, что я не могу привести пример кода. Я далеко от своего ноутбука, и у меня есть только смартфон, чтобы написать текст. Вдали от ноутбука == отпуск :-) - person jboi; 29.08.2013
comment
@asgaroth: я нашел фрагмент кода в ответе по ссылке ниже. Вы должны быть в состоянии скопировать заключительные последовательности. stackoverflow.com /вопросы/18187022/ - person jboi; 29.08.2013
comment
Ситуация, упомянутая в вашем первом абзаце, не представляет собой какую-либо проблему и, конечно же, не объясняет проблему ОП. Все махинации с shutdownOutput() и дополнительными сообщениями EOF совершенно не нужны (если только вы не хотите добиться синхронизированного закрытия, что бывает крайне редко). Достаточно просто закрыть сокет: все ожидающие данные все равно будут доставлены. - person user207421; 14.09.2016

Проблема заключается в том, что клиент и сервер не могут идентифицировать состояние друг друга:

  1. Клиент отправляет данные на сервер, где сервер закрыл соединение
  2. Сервер отправляет/читает данные клиенту, где клиент закрыл соединение

Либо не в состоянии координировать друг с другом, решения могут заключаться в создании надлежащего конечного автомата. Некоторые примеры в Google, если вы ищете (конечный автомат клиента и сервера), дают математически определенные примеры конечного автомата для вашего приложения: надеюсь, что этот комментарий поможет.

Следовательно, бесполезно рассматривать эту проблему с точки зрения решения и, вероятно, начинать использовать такие протоколы, как: telnet и т. Д.

person Harsha    schedule 25.06.2019

Хорошо, теперь я чувствую себя глупо. Я сам закрыл сокет внутри кода сервера. После принятия соединения внутри блока finally выполняется следующее:

try {
    client.close();
} catch (IOException e) {
    e.printStackTrace();
}

Причина, по которой существует этот блок finally, заключалась в том, что я раньше не использовал потоки, поэтому ReceiverThread также обрабатывал соединение и, следовательно, закрывал сокет после его использования. Затем я переместил код в новый поток и забыл удалить этот блок finally!

person asgaroth    schedule 29.08.2013

Вы не можете использовать буферизованный входной поток и поток другого типа в одном и том же сокете. Буферизованный поток будет воровать данные у другого. Прими решение. ObjectInputStream сделает все, что вам нужно. Просто используйте это.

ИЗМЕНИТЬ В вашем редактировании "сокет закрыт" означает, что вы закрыли свой сокет, а затем продолжили его использовать.

person user207421    schedule 29.08.2013
comment
Спасибо за быстрый ответ. Я получил код из какого-то учебника, который мне больше не удается найти. Но это уже сработало один раз. Я думаю, кто-то сказал мне, что BufferedInputStream управляет буфером для сокета, если нет системной буферизации. Но чтобы попробовать ваш совет, что вы имеете в виду под Make Up your kind ? - person asgaroth; 29.08.2013
comment
Теперь я попробовал ваш совет, если я правильно понял, я использовал `clientSocket = new Socket (10.0.2.2, 7777); clientSocket.setTcpNoDelay (истина); out = новый ObjectOutputStream (clientSocket.getOutputStream()); out.writeObject(xmlToSend); out.flush(); ` Но это не работает, у меня та же проблема, о которой я упоминал ранее. Также есть этот пост, где ObjectInputStream и BufferedInputStream используются конкатенировано: Создание буферизованных потоков - person asgaroth; 29.08.2013
comment
@asgaroth Я не вижу ничего «объединенного» в вашей ссылке. Все, что я вижу, это повторение того совета, который я дал вам здесь. - person user207421; 22.03.2016