Java,通过Socket发送大文件正在消耗太多CPU周期并且速度很慢

时间:2015-06-21 16:39:37

标签: java sockets

我正在尝试使用JAVA并在线发现了这个问题。

Java sending and receiving file (byte[]) over sockets

只是出于好奇,我在接受的答案中使用了代码,并且我发现了类似问题的其他代码。我尝试了接受的答案,是的,它的工作原理非常快。但问题是存档文件已损坏。所以这是我试过的其他代码。我的实验代码的垮台是消耗CPU周期并花费更多时间而不是接受的答案(我不知道为什么会这样)。所以这是我的代码。有人可以帮助我更多地优化和改进这些代码。

接受的答案时间= 4毫秒的4 Mb文件。 我的实验花费的时间=同一文件的4秒。

Server.java

public class Server implements Runnable {

 private ServerSocket serverSocket = null;
 private Socket socket = null;
 private ObjectInputStream inStream = null;

 public Server() {

 }

 @Override
 public void run() {

     try {
    serverSocket = new ServerSocket(4445);
    socket = serverSocket.accept();
    DataInputStream dIn = new DataInputStream(socket.getInputStream());
    OutputStream os = socket.getOutputStream();
    DataOutputStream outToClient = new DataOutputStream(os);

    System.out.println("Connected");
    File myFile = new File("lib1.zip");
    long flength = myFile.length();
    System.out.println("File Length"+flength);
    outToClient.writeLong(flength);
    FileInputStream fis;
    BufferedInputStream bis;
    byte[] mybytearray = new byte[8192];
    fis = new FileInputStream(myFile);
    bis = new BufferedInputStream(fis);
    int theByte = 0;
    System.out.println("Sending " + myFile.getAbsolutePath() + "(" + myFile.length() + " bytes)");
       while ((theByte = bis.read()) != -1) {
     outToClient.write(theByte);
     // bos.flush();
     }
    /*int count;
    BufferedOutputStream bos= new BufferedOutputStream(os);
    while ((count = bis.read(mybytearray))>0) {
        bos.write(mybytearray, 0, count);
    }*/

    bis.close();
    socket.close();

} catch (SocketException se) {

    System.exit(0);
} catch (IOException e) {
    e.printStackTrace();
}
 }

 public static void main(String[] args) {
     Thread t = new Thread(new Server());
     t.start();
 }
 }

ReceiveFile.java

 public class RecieveFile {

 public final static int SOCKET_PORT = 4445;      // you may change this
 String SERVER = "127.0.0.1";  // localhost
 ArrayList<String> logmsg = new ArrayList<>();
 public static void main(String[] args) {
     new RecieveFile();
 }

 public RecieveFile() {
     try (Socket sock = new Socket(SERVER, SOCKET_PORT)) {

    System.out.println("Connecting...");
    try (OutputStream os = sock.getOutputStream(); DataOutputStream outToServer = new DataOutputStream(os)) {
        try (DataInputStream dIn = new DataInputStream(sock.getInputStream())) {
            long fileLen, downData;
            int bufferSize = sock.getReceiveBufferSize();

            long starttime = System.currentTimeMillis();
            File myFIle = new File("lib1.zip");
            try (FileOutputStream fos = new FileOutputStream(myFIle); BufferedOutputStream bos = new BufferedOutputStream(fos)) {
                fileLen = dIn.readLong();
                /*for (long j = 0; j <= fileLen; j++) {
                 int tempint = is.read();
                 bos.write(tempint);
                 }*/
                downData = fileLen;
                int n = 0;
                byte[] buf = new byte[8192];
                    while (fileLen > 0 && ((n = dIn.read(buf, 0, buf.length)) != -1)) {
                 bos.write(buf, 0, n);
                 fileLen -= n;
                 //            System.out.println("Remaining "+fileLen);
                 }
                /*while ((n = dIn.read(buf)) > 0) {
                    bos.write(buf, 0, n);
                }*/
                bos.flush();
                long endtime = System.currentTimeMillis();
                System.out.println("File " + myFIle.getAbsolutePath()
                        + " downloaded (" + downData + " bytes read) in " + (endtime - starttime) + " ms");

            }
        }
    }
} catch (IOException ex) {
    Logger.getLogger(RecieveFile.class.getName()).log(Level.SEVERE, null, ex);
}

 }
 }

3 个答案:

答案 0 :(得分:1)

你的解决方案需要花费很多时间,因为你正在阅读一个角色而不是所有的缓冲区。

解决方案是使用类似于链接问题的构造;你得到的关于损坏文件的问题实际上是不可能的,通过CRC检查的格式错误的TCP打包很少发生,我会责怪一个bug。尝试发布您使用的代码。但是如果你担心这个

,你可以在文件及其中的一部分上添加一些哈希检查

答案 1 :(得分:1)

您一次复制一个字节。这很慢。您还要声明一个字节数组但不使用它。试试这个:

int count;
byte[] buffer = new byte[8192]; // or more, double or quadruple it

while ((count = in.read(buffer)) > 0)
{
    out.write(buffer, 0, count);
}

答案 2 :(得分:0)

以下是代码的清理版本,它应该执行得更快,因为它避免了单字节操作:

public class Server implements Runnable {
    @Override
    public void run() {
        try {
            ServerSocket serverSocket = new ServerSocket(4445);
            Socket socket = serverSocket.accept();
            OutputStream os = socket.getOutputStream();
            DataOutputStream dos = new DataOutputStream(os);

            File myFile = new File("lib1.zip");
            long flength = myFile.length();
            dos.writeLong(flength);
            InputStream fis = new FileInputStream(myFile);

            byte[] buf = new byte[16*1024]; // 16K
            long written = 0;

            while ((count = fis.read(buf))>0) {
                dos.write(buf, 0, count);
                written+=count;
            }

            if (written != flength)
                System.out.println("Warning: file changed");
            dos.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
            System.exit();
        }
 }

可能的改进是将NIO与channel.sendTo()一起使用,但这应该已经具有可接受的性能。请注意,在使用较大的字节数组缓冲区时,无需在读取或写入时使用缓冲流。

一个可能的改进是不要长时间使用DataOutputStream,而是将它的8个字节戳到第一个缓冲区(数组)写入。

BTW:在11ms内写入4MB是390MB / s,这比大多数桌面磁盘可以读写的速度快。