2015-05-03 4 views
5

पर बाइट्स कॉपी करने में विफल रहता है, तो मुझे जावा में शून्य-प्रति का उपयोग करके फ़ाइल से सॉकेट में बड़ी फ़ाइलों को स्थानांतरित करते समय कुछ अजीब व्यवहार दिखाई दे रहा है। मेरे वातावरण:FileChannel शून्य-प्रतिलिपि हस्तांतरण सॉकेट चैनल

  • विंडोज 7 64-बिट जेडीके 1.6.0_45 और 1.7.0_79।
  • Centos 6.6 64-बिट JDK 1.6.0_35

क्या कार्यक्रम है: transferFrom और transferTo: ग्राहक प्रतियां एक सॉकेट में कोई इनपुट फ़ाइल, और सर्वर प्रतियां आउटपुट फ़ाइल शून्य-प्रतिलिपि तरीकों का उपयोग कर के लिए सॉकेट। यदि फ़ाइल का आकार अपेक्षाकृत बड़ा है, तो विंडोज़ के मामले में 100 एमबी + और सेंटोस के मामले में 2 जीबी + सर्वर पर सभी बाइट सर्वर तक नहीं पहुंच रहे हैं। क्लाइंट और सर्वर उसी मशीन पर रहते हैं और स्थानीयहोस्ट पते डेटा स्थानांतरित करने के लिए उपयोग किया जाता है।

व्यवहार ओएस के आधार पर अलग है। विंडोज़ पर, ग्राहक सफलतापूर्वक विधि को हस्तांतरण पूरा करता है। स्थानांतरित बाइट्स की संख्या इनपुट फ़ाइल आकार के बराबर है।

long bytesTransferred = fileChannel.transferTo(0, inputFile.length(), socketChannel);

दूसरी ओर सर्वर, प्राप्त बाइट्स की संख्या में कमी की रिपोर्ट।

long transferFromByteCount = fileChannel.transferFrom(socketChannel, 0, inputFile.length());

लिनक्स पर ग्राहक पर bytesTransferred 2Gb भले ही इनपुट फ़ाइल आकार 4Gb है। दोनों विन्यास पर पर्याप्त जगह है।

विंडोज़ पर मैं निम्नलिखित वर्कअराउंड्स में से एक के साथ एक 130 एमबी फ़ाइल स्थानांतरित करने में सक्षम था: 1) सर्वर पर बफर आकार प्राप्त करना और 2) क्लाइंट में थ्रेड नींद विधि जोड़ना। इससे मुझे लगता है कि स्थानांतरण यह सोचता है कि क्लाइंट पर विधि पूरी हो जाती है जब सभी बाइट सॉकेट को भेजते हैं, बफर भेजते हैं, सर्वर पर नहीं। चाहे वे बाइट्स इसे सर्वर पर बनाते हैं या नहीं, यह गारंटी नहीं है, जो मेरे उपयोग के मामले में समस्याएं पैदा करता है।

लिनक्स पर अधिकतम फ़ाइल आकार जो मैं एक ही हस्तांतरण के साथ स्थानांतरित करने में सक्षम हूं, आमंत्रण 2 जीबी है, हालांकि कम से कम ग्राहक सर्वर पर भेजे गए बाइट्स की सही संख्या रिपोर्ट करता है।

मेरे प्रश्न: सर्वर के लिए फ़ाइल की गारंटीकृत डिलीवरी सुनिश्चित करने के लिए क्लाइंट के लिए सबसे अच्छा तरीका क्या है, क्रॉस-प्लेटफ़ॉर्म? विंडोज़ पर sendfile() अनुकरण करने के लिए किस तंत्र का उपयोग किया जाता है?

कोड यह रहा:

क्लाइंट - ZeroCopyClient.java:

import org.apache.commons.io.FileUtils; 

import java.io.*; 
import java.net.*; 
import java.nio.channels.*; 

public class ZeroCopyClient { 

    public static void main(String[] args) throws IOException, InterruptedException { 

     final File inputFile = new File(args[0]); 

     FileInputStream fileInputStream = new FileInputStream(inputFile); 
     FileChannel fileChannel = fileInputStream.getChannel(); 
     SocketAddress socketAddress = new InetSocketAddress("localhost", 8083); 
     SocketChannel socketChannel = SocketChannel.open(); 
     socketChannel.connect(socketAddress); 

     System.out.println("sending " + inputFile.length() + " bytes to " + socketChannel); 

     long startTime = System.currentTimeMillis(); 
     long totalBytesTransferred = 0; 
     while (totalBytesTransferred < inputFile.length()) { 
      long st = System.currentTimeMillis(); 
      long bytesTransferred = fileChannel.transferTo(totalBytesTransferred, inputFile.length()-totalBytesTransferred, socketChannel); 
      totalBytesTransferred += bytesTransferred; 
      long et = System.currentTimeMillis(); 
      System.out.println("sent " + bytesTransferred + " out of " + inputFile.length() + " in " + (et-st) + " millis"); 
     } 

     socketChannel.finishConnect(); 
     long endTime = System.currentTimeMillis(); 

     System.out.println("sent: totalBytesTransferred= " + totalBytesTransferred + "/" + inputFile.length() + " in " + (endTime-startTime) + " millis"); 

     final File outputFile = new File(inputFile.getAbsolutePath() + ".out"); 
     boolean copyEqual = FileUtils.contentEquals(inputFile, outputFile); 
     System.out.println("copyEqual= " + copyEqual); 

     if (args.length > 1) { 
      System.out.println("sleep: " + args[1] + " millis"); 
      Thread.sleep(Long.parseLong(args[1])); 
     } 
    } 
} 

सर्वर - ZeroCopyServer.java:

import java.io.*; 
import java.net.*; 
import java.nio.channels.*; 
import org.apache.commons.io.FileUtils; 

public class ZeroCopyServer { 

    public static void main(String[] args) throws IOException { 

     final File inputFile = new File(args[0]); 
     inputFile.delete(); 
     final File outputFile = new File(inputFile.getAbsolutePath() + ".out"); 
     outputFile.delete(); 

     createTempFile(inputFile, Long.parseLong(args[1])*1024L*1024L); 

     System.out.println("input file length: " + inputFile.length() + " : output file.exists= " + outputFile.exists()); 

     ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
     serverSocketChannel.socket().setReceiveBufferSize(8*1024*1024); 
     System.out.println("server receive buffer size: " + serverSocketChannel.socket().getReceiveBufferSize()); 
     serverSocketChannel.socket().bind(new InetSocketAddress("localhost", 8083)); 
     System.out.println("waiting for connection"); 
     SocketChannel socketChannel = serverSocketChannel.accept(); 
     System.out.println("connected. client channel: " + socketChannel); 

     FileOutputStream fileOutputStream = new FileOutputStream(outputFile); 
     FileChannel fileChannel = fileOutputStream.getChannel(); 
     long startTime = System.currentTimeMillis(); 
     long transferFromByteCount = fileChannel.transferFrom(socketChannel, 0, inputFile.length()); 
     long endTime = System.currentTimeMillis(); 
     System.out.println("received: transferFromByteCount= " + transferFromByteCount + " : outputFile= " + outputFile.length() + " : inputFile= " + inputFile.length() + " bytes in " + (endTime-startTime) + " millis"); 

     boolean copyEqual = FileUtils.contentEquals(inputFile, outputFile); 
     System.out.println("copyEqual= " + copyEqual); 

     serverSocketChannel.close(); 

    } 

    private static void createTempFile(File file, long size) throws IOException{ 
     RandomAccessFile f = new RandomAccessFile(file.getAbsolutePath(), "rw"); 
     f.setLength(size); 
     f.writeDouble(Math.random()); 
     f.close(); 
    } 

} 

अद्यतन 1: लिनक्स कोड पाश के साथ तय की।

अद्यतन 2: एक संभावित कार्यवाही जिसे मैं विचार कर रहा हूं क्लाइंट-सर्वर सहयोग की आवश्यकता है। ट्रांसमिशन के अंत में सर्वर क्लाइंट को प्राप्त डेटा की लंबाई लिखता है जिसे क्लाइंट ब्लॉकिंग मोड में पढ़ता है।

सर्वर से प्रतिक्रिया करता है:

ByteBuffer response = ByteBuffer.allocate(8); 
response.putLong(transferFromByteCount); 
response.flip(); 
socketChannel.write(response); 
serverSocketChannel.close(); 

साथ पढ़ने ग्राहक ब्लॉक:

ByteBuffer response = ByteBuffer.allocate(8); 
socketChannel.read(response); 
response.flip(); 
long totalBytesReceived = response.getLong(); 

नतीजतन, बाइट्स के माध्यम से भेजने के गुजरती हैं और सॉकेट बफ़र्स प्राप्त करने के लिए ग्राहक इंतजार कर रहा है, और यह तथ्य इंतजार में आउटपुट फ़ाइल में बाइट्स को संग्रहीत करने के लिए। आउट-ऑफ-बैंड स्वीकृति को लागू करने की कोई आवश्यकता नहीं है और फ़ाइल की सामग्री उत्परिवर्तनीय होने पर क्लाइंट II.A https://linuxnetworkstack.files.wordpress.com/2013/03/paper.pdf में सुझाए गए अनुसार प्रतीक्षा करने की कोई आवश्यकता नहीं है।

अद्यतन 3 "फ़ाइल का एक ही भाग को फिर से लिखने से पहले समय की राशि" उचित "एक प्रतीक्षा":

एक @EJP द्वारा और @ the8472 उदाहरण फिक्स शामिल संशोधित आउटपुट ट्रेसिंग के बिना लंबाई और फ़ाइल चेकसम सत्यापन दोनों के साथ। ध्यान दें कि एक बड़ी फ़ाइल के लिए सीआरसी 32 चेकसम की गणना करने में कुछ सेकंड लग सकते हैं।

ग्राहक:

import java.io.*; 
import java.net.*; 
import java.nio.*; 
import java.nio.channels.*; 
import org.apache.commons.io.FileUtils; 

public class ZeroCopyClient { 

    public static void main(String[] args) throws IOException { 

     final File inputFile = new File(args[0]); 

     FileInputStream fileInputStream = new FileInputStream(inputFile); 
     FileChannel fileChannel = fileInputStream.getChannel(); 
     SocketAddress socketAddress = new InetSocketAddress("localhost", 8083); 
     SocketChannel socketChannel = SocketChannel.open(); 
     socketChannel.connect(socketAddress); 

     //send input file length and CRC32 checksum to server 
     long checksumCRC32 = FileUtils.checksumCRC32(inputFile); 
     ByteBuffer request = ByteBuffer.allocate(16); 
     request.putLong(inputFile.length()); 
     request.putLong(checksumCRC32); 
     request.flip(); 
     socketChannel.write(request); 

     long totalBytesTransferred = 0; 
     while (totalBytesTransferred < inputFile.length()) { 
      long bytesTransferred = fileChannel.transferTo(totalBytesTransferred, inputFile.length()-totalBytesTransferred, socketChannel); 
      totalBytesTransferred += bytesTransferred; 
     } 

     //receive output file length and CRC32 checksum from server 
     ByteBuffer response = ByteBuffer.allocate(16); 
     socketChannel.read(response); 
     response.flip(); 
     long totalBytesReceived = response.getLong(); 
     long outChecksumCRC32 = response.getLong(); 

     socketChannel.finishConnect(); 

     System.out.println("CRC32 equal= " + (checksumCRC32 == outChecksumCRC32)); 

    } 
} 

सर्वर:

import java.io.*; 
import java.net.*; 
import java.nio.*; 
import java.nio.channels.*; 
import org.apache.commons.io.FileUtils; 

public class ZeroCopyServer { 

    public static void main(String[] args) throws IOException { 

     final File outputFile = new File(args[0]); 

     ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
     serverSocketChannel.socket().bind(new InetSocketAddress(8083));  
     SocketChannel socketChannel = serverSocketChannel.accept(); 

     //read input file length and CRC32 checksum sent by client 
     ByteBuffer request = ByteBuffer.allocate(16); 
     socketChannel.read(request); 
     request.flip(); 
     long length = request.getLong(); 
     long checksumCRC32 = request.getLong(); 

     FileOutputStream fileOutputStream = new FileOutputStream(outputFile); 
     FileChannel fileChannel = fileOutputStream.getChannel(); 
     long totalBytesTransferFrom = 0; 
     while (totalBytesTransferFrom < length) { 
      long transferFromByteCount = fileChannel.transferFrom(socketChannel, totalBytesTransferFrom, length-totalBytesTransferFrom); 
      if (transferFromByteCount <= 0){ 
       break; 
      } 
      totalBytesTransferFrom += transferFromByteCount; 
     } 

     long outChecksumCRC32 = FileUtils.checksumCRC32(outputFile); 

     //write output file length and CRC32 checksum back to client 
     ByteBuffer response = ByteBuffer.allocate(16); 
     response.putLong(totalBytesTransferFrom); 
     response.putLong(outChecksumCRC32); 
     response.flip(); 
     socketChannel.write(response); 

     serverSocketChannel.close(); 

     System.out.println("CRC32 equal= " + (checksumCRC32 == outChecksumCRC32)); 

    } 
} 
+1

'transferTo()' को निर्दिष्ट नहीं है एक ही कॉल में पूरा स्थानांतरण पूरा करें। आपको लूप करना है। – EJP

+0

@EJP बाइट्स क्लाइंट पर ट्रांसफर किए गए इनपुट के समान बाइट गिनती देता है विंडोज़ पर फ़ाइल लंबाई, इसलिए कोई बाइट बाइट नहीं है। मुझे लगता है कि मैं जो देख रहा हूं वह यह है कि बाइट्स ट्रांसफर किए गए रिटर्न जब बाइट्स को बफर में रखा जाता है, न कि जब उन्हें सर्वर पर भेजा जाता है। –

+0

@EJP –

उत्तर

3

समाधान fileChannel.transferFrom से काउंटर लिखने की जाँच करने के लिए है:

import java.io.*; 
import java.net.*; 
import java.nio.*; 
import java.nio.channels.*; 
import org.apache.commons.io.FileUtils; 

public class ZeroCopyServer { 

public static void main(String[] args) throws IOException { 

    final File outputFile = new File(args[0]); 

    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); 
    serverSocketChannel.socket().bind(new InetSocketAddress(8083));  
    SocketChannel socketChannel = serverSocketChannel.accept(); 

    //read input file length and CRC32 checksum sent by client 
    ByteBuffer request = ByteBuffer.allocate(16); 
    socketChannel.read(request); 
    request.flip(); 
    long length = request.getLong(); 
    long checksumCRC32 = request.getLong(); 

    FileOutputStream fileOutputStream = new FileOutputStream(outputFile); 
    FileChannel fileChannel = fileOutputStream.getChannel(); 
    long totalBytesTransferFrom = 0; 
    while (totalBytesTransferFrom < length) { 
     long transferFromByteCount = fileChannel.transferFrom(socketChannel, totalBytesTransferFrom, length-totalBytesTransferFrom); 
     if (transferFromByteCount <= 0){ 
      break; 
     } 
     totalBytesTransferFrom += transferFromByteCount; 
    } 

    long outChecksumCRC32 = FileUtils.checksumCRC32(outputFile); 

    //write output file length and CRC32 checksum back to client 
    ByteBuffer response = ByteBuffer.allocate(16); 
    response.putLong(totalBytesTransferFrom); 
    response.putLong(outChecksumCRC32); 
    response.flip(); 
    socketChannel.write(response); 

    serverSocketChannel.close(); 

    System.out.println("CRC32 equal= " + (checksumCRC32 == outChecksumCRC32)); 

    } 
} 
संबंधित मुद्दे