Неправильная передача файла через Java Socket - PullRequest
0 голосов
/ 15 июля 2011

сегодня днем ​​я написал этот класс, цель которого - предоставить простой способ обмена, отправить файл через TCP-сокет.
Проблема в том, что, несмотря на то, что конечный размер файла правильный, содержимое неверное: именно файл назначениясостоит из различных копий первого буфера, отправленного через сокет.Мой класс прост: он вычисляет Q и R на основе размера буфера и отправляет это число вместе с исходным именем файла клиенту.Я использовал байтовый массив для отправки данных через Socket.

package it.s4sytems.java;  
import java.io.*;  
import java.net.*;  

public class FileOverObjectStream
{
    private File file;
    private int bufferSize = 4*1024*1024; //4MB default, comunque è stabilito dal sender

    private static class Info implements Serializable
    {
        public String fileName;
        public long q;
        public int r;
        public int bufferSize;
    }

    public FileOverObjectStream(File file)
    {
        this.file = file;
    }

    public FileOverObjectStream(File file, int bufferSize)
    {
        this(file);
        this.bufferSize = bufferSize;
    }

    public void sendFile(Socket socket) throws IOException
    {
        socket.getInputStream();
        sendFile( socket.getOutputStream() );
    }

    public void sendFile(OutputStream outStream)throws IOException
    {
        sendFile( new ObjectOutputStream(outStream) );
    }

    public void sendFile(ObjectOutputStream objOutStream) throws IOException
    {
        BufferedInputStream in = new BufferedInputStream( new FileInputStream(file) );
        byte[] buffer = new byte[bufferSize];

        Info info = new Info();
            info.fileName = file.getName();
            info.bufferSize = bufferSize;
            info.q = file.length() / bufferSize;
            info.r = (int) file.length() % bufferSize;
        objOutStream.writeObject(info);

        for(long i=0; i<info.q; i++)
        {
            in.read(buffer);
            objOutStream.writeObject(buffer);
            objOutStream.flush();
        }
        in.read( buffer = new byte[info.r]);
        objOutStream.writeObject(buffer);

        objOutStream.flush();
        in.close();
    }

    public String receiveFile(Socket socket) throws IOException, ClassNotFoundException
    {
        socket.getOutputStream();
        return receiveFile( socket.getInputStream() );
    }

    public String receiveFile(InputStream inStream) throws IOException, ClassNotFoundException
    {
        return receiveFile( new ObjectInputStream(inStream) );
    }

    public String receiveFile(ObjectInputStream objInStream) throws IOException, ClassNotFoundException
    {
        BufferedOutputStream out = new BufferedOutputStream( new FileOutputStream(file) );

        Info info = (Info) objInStream.readObject();
        for(long i=0; i<info.q+1; i++)
        {
            byte[] buffer = (byte[]) objInStream.readObject();
            out.write( buffer );
        }

        out.close();
        return info.fileName;
    }
}

Я создал два класса, чтобы попытаться ...

import it.s4sytems.java.*;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server
{
    public static void main(String arg[]) throws IOException
    {
        ServerSocket ss = new ServerSocket(18000);

        while(true)
        {
            Socket s = ss.accept();

            File file = new File("G:\\HCHCK_72_5.38.part04.rar");
            FileOverObjectStream sender = new FileOverObjectStream(file);
            sender.sendFile(s);
            s.close();
        }
    }
}

и client ...

import it.s4sytems.java.*;
import java.io.*;
import java.net.*;

public class Client
{
    public static void main(String arg[]) throws IOException, ClassNotFoundException
    {
        Socket s = new Socket("localhost", 18000);

        String matricola = "616002424";

        File directory = new File(System.getProperty("user.dir") + "\\" + matricola);
        directory.mkdir();

        File file = File.createTempFile("7897_", null, directory);

        String originalName = new FileOverObjectStream(file).receiveFile(s);

        System.out.println(originalName);

        s.close();


        File file2 = new File(directory, originalName);
        System.out.println( file.renameTo( file2 ) );
        System.out.println( file.getAbsoluteFile());
        System.out.println( file2.getAbsoluteFile());
    }
}

Возможно, это глупость, но я не вижу этого, поэтому мне нужна ваша помощь, пожалуйста.

Спасибо

Ответы [ 2 ]

2 голосов
/ 15 июля 2011

Я не думаю, что ObjectOutputStream подходит в вашем случае использования.Если я что-то пропустил.В общем, попробуйте использовать хорошую библиотеку для ввода-вывода, такую ​​как Apache Commons IO .У него есть методы, которые всегда делают правильные вещи.Посмотрите, например, IOUtils .


Некоторые ошибки, которые нужно выделить (они не произошли бы с хорошей библиотекой)

  • in.read(buffer) не гарантируетсяпрочитать точное количество байтов.Вы должны проверить его результат и записать только правильный номер.
  • Вы записываете буферный объект в ObjectOutputStream с помощью writeObject.Это записывает сериализованный байтовый буфер, а не необработанную последовательность байтов.
0 голосов
/ 15 июля 2011

Ваш код ObjectInput / OutputStream имеет недостатки во всех отношениях, которые заметил Алекс. Я бы вообще не использовал его, я бы просто использовал необработанный ввод-вывод. Канонический способ копирования потока в Java выглядит следующим образом:

int count;
byte[] buffer = new byte[8192]; // or more, but megabytes is pointless as the network will packetize anyway
while ((count = in.read(buffer)) > 0)
{
  out.write(buffer, 0, count);
}

Используйте этот код при отправке и получении файла. Если вы хотите отправить> 1 файл на каждое соединение, вам нужно все это поставить перед префиксом, отправив имя и длину файла, что вы можете сделать с DataOutputStream.writeUTF()/writeLong() и DataInputStream.readUTF()/readLong() на приемнике, и изменить элемент управления циклом для точного чтения столько байтов:

long remaining = size; // the file size read from the network
while ((count = in.read(buffer, 0, remaining > buffer.length ? buffer.length : (int)remaining)) > 0)
{
    out.write(buffer, 0, count);
    remaining -= count;
}
...