Java, получаю странное исключение сокета: «Сломанный канал» и «Время ожидания операции истекло»

#java #sockets #exception #tcp

#java #сокеты #исключение #tcp

Вопрос:

У меня есть клиент-серверное приложение, которое имеет 16 потоков (8 клиентских, 8 серверных), и у каждого есть TCP-соединение в соответствии 1-1 (таким образом, 8 потоков TCP).

У меня есть сервер, отправляющий X количество случайных байтов, а затем закрывающийся. Тем временем клиент просто считывает X объем данных, а затем закрывается. X предопределено. Я также использую dummynet для регулирования пропускной способности, но я оставляю канал с достаточной пропускной способностью (100 Мбит / с). Иногда это работает нормально, в других случаях я получаю эти исключения. Я переношу 1 ГБ, равномерно распределенный по всем 8 соединениям, примерно за 3,5 минуты.

Клиент выдает это исключение:

 java.net.SocketException: Operation timed out
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at miccbull.parralleltcp.client.StreamWorker.run(StreamWorker.java:43)
  

Сервер выдает это исключение:

 java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:124)
at miccbull.parralleltcp.server.TransmissionWorker.run(TransmissionWorker.java:51)
  

Серверный код:

 public void run() {

    OutputStream os = null;

    try {
        socket = sSocket.accept();
        os = socket.getOutputStream();
    } catch (IOException e2) {
        e2.printStackTrace();
    }

    //send the data
    for(int i = 0; i < Server.TRANSMISSIONS; i  ){
        System.out.println(sSocket.getLocalPort()   " sending set "   (i 1)   " of "   Server.TRANSMISSIONS);

        try {
            os.write(new byte[Server.FILE_SIZE/Server.numStreams/Server.TRANSMISSIONS]);
            os.flush();
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Closing server (at write bytes) with socket id: "   sSocket.getLocalPort());
        } 
    }

    System.out.println("Worker "   sSocket.getLocalPort()   " done");

    //close the socket
    try {
        socket.close();
        sSocket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
  

Клиентский код:

     public void run(){

    byte[] bytes = new byte[1024];  //number of bytes to read per client stream
    InputStream is = null;

    //connect to server
    try {
        connectionSocket = new Socket("localhost", id);
        is = connectionSocket.getInputStream();
    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }


    //read file from server
    int value = 0;
    int bytesRead = 0;
    try {

        while(bytesRead < (Client.FILE_SIZE/Client.NUM_STREAMS)){
            value = is.read(bytes, 0, 1024);
            if(value == -1){
                System.out.println("******************************** Read is -1 for client "   id);
            }
            else if(value == 0){
                System.out.println("******************************** Read is 0 for client "   id);
            }
            bytesRead  = value;
        }
    } catch (IOException e) {
        System.out.println("**** Exception in read in client "   id   " and value value was: "   value);
        if(bytesRead == (Client.FILE_SIZE/Client.NUM_STREAMS)){
            System.out.println("************ NOT ACTUALLY BAD"   id);
        }
        e.printStackTrace();
    }

    //Finished download
    Client.workerDone();
    System.out.println("Worker "   id   " done received "   bytesRead);
  

Что вызывает эти исключения?

Ответ №1:

Ваш клиент никогда не прекращает чтение, потому что вы не проверяете, возвращает ли read() < 0.

Комментарии:

1. Привет, я прекращаю чтение, когда получаю все байты, которые я ожидаю от сервера, в частности, с этим условием bytesRead < (Client.FILE_SIZE/Client.NUM_STREAMS) . Тем не менее, я ввел туда несколько проверок значения, возвращаемого из read(), и оно никогда не было < 0 во время моего эксперимента. Что может быть причиной для беспокойства??

2. Это становится очень неприятным. Спасибо за вашу помощь.

3. @Michael тем не менее, ваш код неверен. Что, если сервер отключится перед отправкой всех ожидаемых вами данных? Ваш код не обнаружит это и будет зацикливаться вечно.

Ответ №2:

Вот что я вижу в вашем коде:

  1. Я проверил, что каждый вызов обоих socket.getOutputStream() и socket.getInputStream() возвращает другой поток, связанный с сокетом. Вы должны вызывать их каждый раз для каждого сокета и помещать результат в локальную переменную.

     OutputStream outputStream = socket.getOutputStream();
    // send the data
    for (int i = 0; i < 10; i  ) {
        ....
        outputStream.write(...);
      
  2. Есть несколько мест, куда вы должны вернуться из своего run() метода, но я подозреваю, что вы это знали.

  3. Вы не проверяете возвращаемое значение из read() , чтобы увидеть, возвращает ли оно -1 при EOF. Вы не должны просто предполагать, что получаете правильное количество байтов.

  4. Я предполагаю, что вы пишете блоки из нулевых символов по какой-то причине.

Комментарии:

1. 1. «Я проверил, что каждый вызов как socket.getOutputStream(), так и socket.getInputStream() возвращает другой поток, связанный с сокетом». Я только что убедился в обратном из исходного кода. Так что, в конечном счете, это вообще не его проблема. 1.1. Очистка такого потока ничего не дает.

2. @Gray Привет, я думал, что потоки были одинаковыми, но я поместил их в локальные переменные, как вы предложили, и я проверил значения из read (). Я только что запустил тест и получил исключение BrokenPipe на клиенте с самым последним прочитанным значением 162. Я отправляю случайные байты, потому что это приложение предназначено только для тестирования производительности. Видите ли вы что-нибудь еще, что может вызывать подозрения?

3. @EJP, ха, у меня нет доступа к исходному коду JDK на моем Mac (grumble), но объекты имеют разные toString() значения, т.Е. Object.hashCode() Так что это разные объекты, если я чего-то не упускаю. Я использую Sun JDK 1.6.0_26. Спасибо за read исправление.

4. @Michael, какие значения от read ты ожидаешь? Из-за буферизации и прерывания пакетных данных вы не будете получать полное чтение 1024 каждый раз. read не блокируется, пока не заполнится весь буфер. Если вы пытаетесь отправить «сущность» через, я бы рекомендовал использовать сериализацию и запись и чтение объекта. В противном случае вам придется самостоятельно обслуживать буфер с помощью BufferedInputStream и т.д..

5. @Gray Меня устраивает любое положительное значение из read() , поскольку я просто добавляю это значение к общей сумме ( value ), которая уже была прочитана ( bytesRead ). Затем, когда bytesRead становится значением, которое я ожидаю от сервера, run завершается. Я только что подтвердил, что клиент действительно выдает это исключение «Время ожидания операции истекло» до того, как он прочитал все данные с сервера. Пока спасибо за вашу помощь!