【问题标题】:Jar File is corrupt after sending over socket通过套接字发送后 Jar 文件损坏
【发布时间】:2013-11-16 23:08:27
【问题描述】:

所以我正在构建一个需要内置自动更新功能的程序,因为我已经完成并对其进行了测试,似乎当我通过套接字发送 jar 文件并将其写入新制作的 jar 时文件丢失 5KB(每次......即使大小发生变化)大小并损坏。

这是我的代码:

package server.update;

import java.io.*;
import java.net.Socket;

public class UpdateThread extends Thread
{
BufferedInputStream input; //not used
BufferedInputStream fileInput;
BufferedOutputStream output;

public UpdateThread(Socket client) throws IOException
{
    super("UpdateThread");
    output = new BufferedOutputStream(client.getOutputStream());
    input = new BufferedInputStream(client.getInputStream());
}

public void run()
{
    try
    {
        File perm = new File(System.getProperty("user.dir")+"/GameClient.jar");
        //fileInput = new BufferedInputStream(new FileInputStream(perm));
        fileInput = new BufferedInputStream(new FileInputStream(perm));

        byte[] buffer = new byte[1024];
        int numRead;
        while((numRead = fileInput.read(buffer)) != -1)
            output.write(buffer, 0, numRead);

        fileInput.close();
        input.close();
        output.close();
        this.interrupt();
    }
    catch(Exception e)
    {e.printStackTrace();}
}
}

这个类将等待来自客户端的连接,然后在连接后立即将更新推送给它们。 File Perm 是我要发送的 jar 文件,无论出于何种原因,它似乎要么错过了最后 5 个字节,要么客户端没有读取最后 5 个字节(我不知道是哪个)。这里是客户端接收信息的类:

public void getUpdate(String ip) throws UnknownHostException, IOException
{
    System.out.println("Connecting to update socket");
    update = new Socket(ip,10004);
    BufferedInputStream is = new BufferedInputStream(update.getInputStream());
    BufferedOutputStream os = new BufferedOutputStream(update.getOutputStream());

    System.out.println("Cleaning GameClient.jar file");
    File updated = new File(System.getProperty("user.dir")+"/GameClient.jar");
    if(updated.exists())
        updated.delete();
    updated.createNewFile();

    BufferedOutputStream osf = new BufferedOutputStream(new FileOutputStream(updated));

    System.out.println("Writing to GameClient.jar");
    byte[] buffer = new byte[1024];
    int numRead = 0;
    while((numRead = is.read(buffer)) != -1)
        osf.write(buffer, 0, numRead);

    System.out.println("Finished updating...");
    is.close();
    os.close();
    update.close();
    osf.close();
}

感谢任何帮助。谢谢!

【问题讨论】:

  • 在几乎所有语言中,在所有平台上,您都需要在 .Close 之前调用 .Flush 方法,以确保将最后的剩余部分推送(到磁盘或网络)。出于某种原因,.Close 从不调用 .Flush。
  • @MDR 不真实。请参阅 FilterOutputStream.close() 的合同。在任何设计良好的类库中,close 应该确保刷新。

标签: java sockets jar inputstream outputstream


【解决方案1】:

你有太多的关闭。删除update.close()is.close(). 这两个都关闭了套接字,这样可以防止缓冲流“osf”在关闭时被自动刷新。关闭输入流或输出流或套接字会关闭另一个流和套接字。因此,您应该只关闭您包裹在套接字周围的最外层输出流,在本例中为 osf,,并且可以确定在 finally 块中的套接字本身。

【讨论】:

    【解决方案2】:

    感谢 MDR 的回答,成功了!!

    我不得不更改 UpdateThread 类中的以下代码行:

    之前:

    fileInput.close();
    input.close();
    output.close();
    this.interrupt();
    

    之后:

    fileInput.close();
    output.flush();
    output.close();
    input.close();
    this.interrupt();
    

    您必须在关闭之前刷新流,我也切换了顺序,因为如果您关闭连接到套接字的输入流,它将关闭套接字,然后不会继续关闭输出流或刷新它。

    再次感谢!

    【讨论】:

    • 这不是问题。问题是所有额外的关闭,还包括您没有包含在引用代码中的“update.close()”。您不必在关闭之前刷新流:它会自动发生。
    【解决方案3】:

    您是否考虑过使用http library 将所有连接处理和读/写委托给已知的工作代码?你在这里重新发明了很多轮子。此外,在某些时候,您将希望确保您收到的内容是真实的且完好无损(您通过加载类来做到这一点,这有点危险,尤其是当您以明文形式交换数据时!) ,使用库及其方法将允许您选择 HTTPS,从而允许 TLS 完成您的大部分工作。

    我还建议您的服务器提前告诉客户端一些元数据,不管是内容长度,也可能是哈希或校验和,以便客户端可以检测到传输失败。

    This question 似乎也有与您的情况相关的答案。祝你好运!

    【讨论】:

    • 这与您用于分发代码的渠道无关。
    • 我不明白这是对所提问题的回答。 OP 不需要使用 HTTP。虽然他可能需要其中一些东西(检查内容是否完好无损并且来自真实来源),但还有其他方法可以实现它们。此外,这些担忧与问题是正交的......如果你看看真正的答案是什么。
    • 当然还有其他方法可以实现他问题的各个方面。 OP也不需要使用IP;但这样做可能会提高他的生活质量。他的基本问题是如何在通过网络链接传输代码时避免完整性问题,而我的回答确实解决了这个问题。
    • 不,它没有。他的基本问题是关于他的代码中的一个特定错误。他使用 HTTP 的等效代码中可能有完全相同的错误。更改为 HTTP 并不能解决他的问题。看看其他答案!!
    • @StephenC 在close() 之前没有说过要打电话给flush()。 OP 和 MDR 都错了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-08
    • 1970-01-01
    • 2015-05-27
    • 1970-01-01
    • 2015-04-16
    • 2011-02-24
    相关资源
    最近更新 更多