2011-11-13 4 views
1

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

Я до сих пор не потрудился изучить 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(); 
     } 
    } 
} 

ответ

2

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() бессмысленно. Следующая строка генерирует исключение, если файл там отсутствует, и вы все равно должны обрабатывать это исключение, так зачем же все это делать снова?

+0

Er ... да Я новичок в NIO, а также в JDK 7 API. Спасибо за вашу помощь. Я попробую эти изменения. Что относительно buffer.rewind()? Нужно ли это использовать? – bgroenks

+0

И не закрывал ли SocketChannel закрытие базового Socket? – bgroenks

+0

@ ghostsoldier23 Это не API JDK 7. Большая часть из них - API JDK * 1.4 *. Конечно, закрытие канала закрывает сокет, как говорится в Javadoc, но вы этого не делаете. Единственное, что вы закрываете, это FileChannel, и это только на сервере. Таким образом, вам не хватает трех закрытий. Вам не нужен 'buffer.rewind()', только то, что я разместил. – EJP