缓冲区 InputStream 未读取最后一个数据包

BufferInputStream not reading the last packet

我正在尝试将 apk 文件从一台设备发送到另一台设备。基本上,要发送的用户有一个带有他安装的应用程序(非系统应用程序)的 gridview,当用户按下其中一个应用程序的图标时,它会将其发送到另一台设备。文件被缓冲成更小的数据包。

我正在做一些系统输出来检查发生了什么,同时发送方设备正在发送 X 个数据包,接收方收到的数据包少于 X 个(这取决于正在发送的应用程序,所以这就是我写的原因X)。我认为这是最后一个丢失的。

我的缓冲区的长度是4096,而"last packet"只有2115。在接收端,它没有抛出读写文件后的系统输出。所以程序卡在了阅读指令上。 我的问题是为什么最后一个数据包没有被传输?

这是我的代码。

客户端 - apk 的发送者。

     public class SendThread implements Runnable{

    @Override
    public void run() {
        try {

            if(clientSocket==null){
                clientSocket=new Socket(serverIP,porto);
                System.out.println("client comm - Created the client socket..");
            }

            System.out.println(" client comm, i am here in the send thread, going to create the outputstream object");
            if(out==null){
                out = new ObjectOutputStream(clientSocket.getOutputStream());
                System.out.println(" Client COMM SEND THREAD - JUST created the outputstream .");
            }


            File apkToSend=new File(filePath);
            byte[] buffer = new byte [4096];
            BufferedInputStream bis=new BufferedInputStream(new FileInputStream(apkToSend));
            int count;
            int total=0; 
            while((count=bis.read(buffer,0,4096))!=-1){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                out.write(buffer,0,count);
                total+=count;
                out.flush();
                System.out.println("Client Comm send thread - already sent this ammount : "+total);
            }

            bis.close();
            out.flush();
            System.out.println("clientComm send thread - Just sent the message ! i am after the out.writeOBject. Not saving it to a string yet.");


        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

现在是服务器端——接收文件的设备..

    public class ReceiveThread implements Runnable{

    @Override
    public void run() {
        try {
            if(socket==null){
                socket=serverSocket.accept();
                System.out.println("serverComm - accepted the socket from the client.");
                out=new ObjectOutputStream(socket.getOutputStream());
                out.writeObject("Hello client, i the server, sending you this message because i want to. SO that you unlock from the input stream creation");
                System.out.println("SErverComm Created the output stream and sent a HEllo message to the client ");
            }

            if(in==null){
                in = new ObjectInputStream(socket.getInputStream());

            }
            while(!serverSocket.isClosed()){
                try {
                    if(!handshakeDone){
                        System.out.println("server comm receive thread - INSIDE THE WHILE CICLE, before the in.readObject");
                        String message = (String) in.readObject();
                        System.out.println("ServercomM receive thread - just did the handshake message control");
                        handshakeDone=true;
                    }else{

                        System.out.println("server comm receive thread - INSIDE THE WHILE CICLE, before the in.readObject");
                        File apkReceived = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/testeReceiveServerComm.apk");
                        byte[] buffer = new byte [4096];
                        BufferedInputStream bis=new BufferedInputStream(in);
                        FileOutputStream fos=new FileOutputStream(apkReceived);
                        int count=0;
                        int total=0;
                        while((count=bis.read(buffer,0,4096))!=-1){

                            fos.write(buffer,0,count);
                            total+=count;
                            System.out.println("Server Comm receive thread - already received this ammount : "+total);

                        }
                        ;
                        System.out.println("Already received everything ! ");
                        fos.flush();
                        bis.close();
                        fos.close();
                        System.out.println(" server comm receive thread - already read the object !!!!!!!!!!!!!!");

                    }

                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这里有很多谬论。

  1. the sender device is sending X packets meanwhile, the receiver is receiving less than X packets

    您没有对数据包进行计数,所以您不可能知道这一点。在任何情况下,接收到的 'packets' 的数量与 TCP 中发送的数量无关。重要的是 字节 的数量,这里没有证据表明您丢失了任何字节。您所看到的是,当您的缓冲区大小为 4096 时,最后一次读取返回了 2115 个字节。这意味着输入文件的大小为 N*4096+2115,而不是 4096 的倍数。

  2. 您正在使用 ObjectInputStreamObjectOutputStream 但您只发送字节。您可以为此使用套接字自己的输入和输出流:无需支付字节流的额外开销。

  3. 你毫无意义地包装了一个 BufferedInputStream *aroundtheObjectInputStream`。应该是反过来的。
  4. 旅游代码中的Thread.sleep()简直就是浪费时间。和 space。删除它。
  5. 您正在套接字关闭后刷新输出流。您应该关闭 output 流,而不是输入流,关闭它会使刷新变得多余。
  6. 您还必须捕获并忽略由 flush() 产生的 IOException,除非您非常幸运并且没有什么可刷新的。我不知道这怎么可能,因为您的文件不是缓冲区大小的倍数。
  7. 您正在使用 while (!serverSocket.isClosed()) 作为从接受的套接字读取的终止测试。这毫无意义。
  8. 您的接收线程同时接受和处理单个连接。这使您的整个服务器成为单线程。应该有一个用于接受的线程,以及处理每个接受的套接字的线程。

您需要仔细阅读网络教程,例如 Oracle Java 教程的自定义网络部分。这不是写网络代码的方法。