通过从桌面到 android 服务的套接字改进数据传输

Improve Data Transfer through socket from desktop to android service

我是 Socket 编程的新手,我在网上搜索了很多,这让我更加困惑。 所以我想将数据从我在 Java 中编写的桌面应用程序传输到我的 Android 设备。 这是逻辑: 我从我电脑上的应用程序启动一个服务器套接字,我从 android 连接到这个服务器。 在 android 中,它连接并在服务中创建套接字,然后在服务中,有一个等待接收数据的 while 循环。 当它收到数据时,它会显示进度通知并获取数据。

但是这里有问题:

  1. 有时在我的桌面应用程序中,它会转到 finally 子句并显示传输完成消息。但是我的 android 设备中的数据传输尚未结束,然后它突然停止传输。当文件很大时,这种情况会发生更多。但对于小文件没关系 [在@greenapps 的帮助下修复]

  2. 当它开始接收文件时,android OS 变得太慢以至于我无法用我的 phone 做任何事情并且 LMK 开始终止进程一次又一次。甚至启动器也被杀死。我用了一个线程为什么会这样? [已修复] 我在每个循环中调用 updateProgressNotif() 的频率太高了。我更改了代码,如果进度超过 5%,则更新进度条。为其他人编辑代码,如果他们想使用代码

  3. 数据传输缓慢。传输 25MB 数据大约需要 15-20 秒。我认为它应该快得多?有什么提高速度的方法吗?

代码如下:

代码编辑 1 : 我使用了@greenapps 的建议并优化了我的代码。它现在发送文件完全正常,发送前后的文件长度在桌面应用程序和 android 接收前后的应用程序文件大小相同。

代码编辑 2 : 我删除了 BufferedReaders,因为它们没用。我已经在读取 64*1024 字节,因此不需要 BufferdReader。此外,我使用了@greenapps 的建议,并使用 readLine() 进行 "name:size" 编码。

代码编辑 3 : 增加了用户 select 多个文件的能力,现在代码知道应该从输入流中读取多少而不读取超过文件大小。

桌面应用程序文件传输代码: 首先它发送文件的名称和长度,然后用 Thread.Sleep 暂停,然后发送文件。例如,它首先发送:myfile.txt:3456。 因为我需要知道文件的名称和长度才能在通知中显示我收到的文件的名称。还有在进度条中使用它的长度

public class SendFile extends Thread {
private Socket client = null;
private Main mainFrame;
private File[] files;
FileInputStream fis = null;

public SendFile(Socket client, Main main, File[] files){
    this.mainFrame = main;
    this.client = client;
    this.files = files;
}

public void run(){
    try {
        mainFrame.btnChooseFile.setEnabled(false);
        for(int i=0; i<files.length; i++){
            File file = files[i];
            String fileInfo = file.getName() + ":" + file.length() + "\n";
            client.getOutputStream().write(fileInfo.getBytes("UTF-8"), 0, fileInfo.getBytes("UTF-8").length);
            client.getOutputStream().flush();
            Thread.sleep(200);
            fis = new FileInputStream(file);
            byte[] fileBuffer = new byte[64*1024];
            int bytesRead;
            int current = 0;
            while ((bytesRead = fis.read(fileBuffer)) != -1) {
                current += bytesRead;
                client.getOutputStream().write(fileBuffer, 0, bytesRead);
            }
            client.getOutputStream().flush();
            fis.close();
            Thread.sleep(300);
        }
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }finally{
        mainFrame.log("File Sent Successfully");
        mainFrame.btnChooseFile.setEnabled(true);
    }

} }

Android应用程序文件接收代码:该线程在服务中运行。所以当我从桌面发送文件时。它会自动显示通知并开始接收文件,即使应用程序未打开但 android 变得如此缓慢

private class WaitForFile extends Thread{
    @Override
    public void run() {
        FileOutputStream fos;
        BufferedReader in;
        String name;
        int fileSize;
        try {
            while(true) {
                String fileInfo;
                in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
                fileInfo = in.readLine();
                name = fileInfo.split(":")[0];
                fileSize = Integer.parseInt(fileInfo.split(":")[1]);
                final String fileName = name;
                final int size = fileSize;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        createGetNotification(fileName);
                    }
                });
                String PATH = Environment.getExternalStorageDirectory() + "/mydownloads/";
                File file = new File(PATH);
                file.mkdirs();
                File outputFile = new File(file, fileName);
                fos = new FileOutputStream(outputFile);
                InputStream is = con.getInputStream();
                byte[] fileBuffer = new byte[64*1024];
                int bytesRead;
                int current = 0;
                float prevIncr = 0;
                int bytesToRead = Math.min(fileBuffer.length, size);
                while ((bytesRead = is.read(fileBuffer, 0, bytesToRead)) != -1) {
                    current += bytesRead;
                    fos.write(fileBuffer, 0, bytesRead);
                    bytesToRead = Math.min(fileBuffer.length, size-current);
                    float incr = ((float)current/(float)size)*100;
                    if(incr - prevIncr > 5) {
                        final float increment = incr;
                        prevIncr = increment;
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                updateProgressNotif(increment);
                            }
                        });
                    }
                    if(current == size)
                        break;
                }
                if(size == current) {
                    fos.flush();
                }else{
                    outputFile = null;
                }
                final File receivedFile = outputFile;
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        doneGetNotification(receivedFile);
                    }
                });

                fos.close();

            }
        } catch (Exception e) {
                e.printStackTrace();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        stopForeground(true);
                        disconnectedNotif();
                        stopSelf();
                    }
                });
        }
    }

}

如有任何帮助,我们将不胜感激

答案在评论里

传输文件名和大小以及使用 sleep() 都不好。例如,您现在发送 myfile.txt:3456。但是你最好发送像 myfile.txt:3456\n 这样的行并删除 sleep()。然后在客户端中使用 readLine() 来读取该行。 getFileSize() 的拆分也可以做得更好。您可以完全取消该功能,只使用 Integer.valueOf().

摆脱:

  1. 发送文件信息后flush()
  2. 之后的sleep()
  3. exists()delete() 调用。
  4. 最后的 flush() 个调用,替换为 close() 个调用。
  5. 如果可能,进度监视器。

(1) 到 (4) 没有任何用处,(5) 浪费的时间比它告诉你的要多。

我用DataInputStream

解决了问题
InputStream is = receive.getInputStream();
DataInputStream dis = new DataInputStream(is);
final String fileName = dis.readUTF();
final long size = dis.readLong();
...

对于发送方,使用 DataOutputStream

无需将 Thread.Sleep() 与 DataStreams 一起使用,发送文件更加可靠。