2011-11-13 44 views
1

我在使用NIO框架在主机和客户端之间通过SocketChannel发送数据时遇到了问题。NIO挂起问题?

我从来没有真正困扰过之前学习NIO,但随着java.nio.files包和其他各种改进的介绍,我想我会试试看。

我能够得到SocketChannel和ServerSocketChannel连接正常,但实际的数据传输行为非常奇怪。它永远不会在客户端正确完成,在最终读取后总是挂起。此外,它有时会读取不正确数量的数据(太多或太少),甚至导致Windows资源管理器发疯,从字面上分配系统的所有内存,导致计算机崩溃。

下面是代码(它是测试代码)我有现在:

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

SocketChannelServerSocketChannel.accept()所得是连接。这是不可能的。 isConnected()测试毫无意义。

您的服务器I/O代码不正确。信道的写操作必须由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

您的客户端I/O代码与您的服务器I/O代码具有相同的问题。

您没有关闭服务器中的SocketChannel,因此客户端永远不会停止从连接读取数据。

您还没有关闭客户端中的SocketChannel

File.exists()测试毫无意义。如果文件不在那里,那么下面这行会引发一个异常,而且你必须处理这个异常,那为什么还要这样呢?

+0

呃...是的,我是NIO以及JDK 7 API的新手。尽管感谢您的帮助。我会尝试这些改变。怎么样buffer.rewind()?这是否需要使用? – bgroenks

+0

并且不会关闭SocketChannel关闭底层套接字? – bgroenks

+0

@ ghostsoldier23这不是JDK 7 API。大部分是JDK * 1.4 * API。正如它在Javadoc中所说,关闭通道当然关闭了插座,但是你没有这样做。你关闭的唯一的东西是FileChannel,并且只在服务器中。所以你缺少三个关闭。你不需要'buffer.rewind()',就是我发布的内容。 – EJP