NIO Проблемы с зависанием?

У меня проблемы с отправкой данных через 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 ответ

Решение

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() подключен: опять же, тест бессмыслен. Если бы он не был связан, был бы ConnectException на connect() вызов.

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

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

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

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

Другие вопросы по тегам