2011-11-13 73 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