NIO Проблемы с зависанием? - PullRequest
1 голос
/ 14 ноября 2011

У меня проблемы с отправкой данных через SocketChannels между хостом и клиентом с использованием инфраструктуры NIO.

Мне никогда не удавалось изучать NIO до сих пор, но с появлением пакета java.nio.files и другими различными улучшениями я подумал, что попробую.

Я могу заставить SocketChannel и ServerSocketChannel нормально соединяться, но фактическая передача данных действует очень странно. Он НИКОГДА не заканчивается правильно на стороне клиента, всегда зависая после окончательного прочтения. Кроме того, иногда он читает неправильный объем данных (слишком много или слишком мало) и даже приводит к тому, что Windows Explorer сходит с ума и выделяет буквально ВСЕ системную память, приводя к поломке компьютера.

Вот код (это тестовый код) у меня сейчас:

package bg.jdk7.io;

import static java.nio.file.StandardOpenOption.*;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class NetIO {

static volatile long filesize;

public static void main(String[] args) {
    new Thread(new Client()).start();
    try {
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(5555));
        SocketChannel sc = ssc.accept();
        if(sc.isConnected()) {
            ByteBuffer buff = ByteBuffer.allocate(10240);
            Path fp = Paths.get(System.getProperty("user.home")+"\\Documents\\clip0025.avi");
            if(Files.exists(fp)) {
                FileChannel fc = (FileChannel) Files.newByteChannel(fp, StandardOpenOption.READ);
                long tot = Files.size(fp);
                long run = 0;
                int read = 0;
                int prog = 0;
                while((read = fc.read(buff))>0) {
                    buff.rewind();
                    sc.write(buff);
                    run+=buff.position();
                    int last = prog;
                    prog = (int)(((double)run/tot)*100);
                    if(prog !=last) {
                        System.out.println(prog + "%");
                    }
                    buff.flip();
                }
                fc.close();
                System.out.println("Sending completed");
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

static class Client implements Runnable {

    public void run() {
        try {
            SocketChannel sc = SocketChannel.open();
            sc.connect(new InetSocketAddress("localhost",5555));
            if(sc.isConnected()) {
                Path dpf = Paths.get("\\NIO_TESTING\\");
                Path dp = Paths.get(dpf+"\\clip.avi");
                Files.createDirectories(dpf);
                FileChannel fc = (FileChannel)  Files.newByteChannel(dp, CREATE, WRITE, TRUNCATE_EXISTING);
                ByteBuffer buff = ByteBuffer.allocate(10240);
                int read;
                int total = 0;
                while((read = sc.read(buff))>0) {
                    total+=read;
                    buff.rewind();
                    fc.write(buff);
                    System.out.println(fc.size());
                    buff.flip();
                    if(total == filesize) System.out.println("File data received successfully...");
                }
                System.out.println("Completed successfully");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1 Ответ

2 голосов
/ 14 ноября 2011

A SocketChannel в результате ServerSocketChannel.accept() подключено . Этого не может быть. Тест isConnected() бессмыслен.

Ваш код ввода / вывода на сервере неверен. Записью канала должен предшествовать buffer.flip(), а затем buffer.compact(). Канонический способ копирования с одного канала на другой заключается в следующем (обратите внимание, что это работает правильно в EOS, даже если в буфере все еще находятся ожидающие данные):

while (in.read(buffer) >= 0 || buffer.position() > 0)
{
  buffer.flip();
  out.write(buffer);
  buffer.compact();
}

Подобно моему первому абзацу, SocketChannel, полученный в результате SocketChannel.open(), за которым следует SocketChannel.connect() , подключен : опять же, тест не имеет смысла. Если бы он не был подключен, на вызове connect() был бы ConnectException.

Ваш код ввода / вывода клиента имеет те же проблемы, что и код ввода / вывода вашего сервера.

Вы не закрываете SocketChannel на сервере, поэтому клиент никогда не прекратит чтение из соединения.

Вы также не закрываете SocketChannel в клиенте.

Тест File.exists() не имеет смысла. Следующая строка выдаст исключение, если файла там нет, и вы все равно должны обработать это исключение, так зачем делать все это снова?

...