android(java) Socket file transfer (readfull)

The file transfer function is used in recently written projects, so I searched many relevant blogs, and the articles I found are similar.
Can their demo run? It can also run, but it is limited to some small files, such as a txt document with hundreds of K to. However, the amount of data we actually need to transmit is much larger than this number. We usually record a small video with the size of tens of M. therefore, we can't solve the practical problems if we really apply those methods.
Because I'm a beginner of android, I'm also learning more knowledge and writing this project while building an app, but this function has tortured me for many days. If this function can't be realized, the subsequent work can't be carried out.
I used other people's methods in my project to transmit a video file of tens of M, and always reported an error called "Broken pipe". I think the channel is terminated because the amount of data transmitted is too large. However, I found the problem when I checked something accidentally. There was a problem at the receiving end, resulting in abnormal transmission. In a large number of blog posts or materials I checked, I used the read() method to read the data from the sending end, but the read() method only read the data in the byte stream, read it with read(), and may return before reading it, As a result, some data cannot be read normally. At this time, the application of readfully can solve this problem.
(if you don't use readfull, you can also use readByte method, but in the actual measurement, I transmitted a video in less than one minute, about 30M. It took seven minutes to send it, which is particularly inefficient.)
You can see the difference between read and readfull: https://blog.csdn.net/yangzhihello/article/details/8078684.
The effect is as follows:

server:

client:


Next, let's put the code of the demo. In order to show this function more directly, only the function of transmitting data from sever to client is written in the demo. As for optimization code, you can add it yourself.

MainActivity:

public class MainActivity extends AppCompatActivity {

    private Button client;
    private Button server;
    static TextView filename;
    static TextView progress;
    static int vis;
    File file;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        client=findViewById(R.id.btn_client);
        server=findViewById(R.id.btn_server);
        progress=findViewById(R.id.text_progress);
        filename=findViewById(R.id.text_file);
        /*During the actual measurement, it is found that the android 8.1 mobile phone cannot use Environment.getExternalStorageDirectory() when reading the file
        Read the files in the path, so we need to get the android version number of the mobile phone in advance, and then do different operations on it*/
        char Vision[] = null;
        Vision = Build.VERSION.RELEASE.toCharArray();
        //Get android version number
        for (int i = 0, j = 1; (i < Vision.length) && (Vision[i] != '.'); i++, j = j * 10) {
            vis = vis * j + ((int) Vision[i] - 48);
        }
        //Get permissions dynamically
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
        }
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
        }
        //Perform different operations according to the version to obtain the file
        //The file name and path must be set correctly, otherwise the following operations cannot be carried out
        if(vis>9){
            file=new File(Environment.getExternalStorageDirectory().getPath()+"/test123/1.mp4");
        }
        else{
            file=new File("sdcard//storage/emulated/0/test123/"+"1.mp4");
        }
        client.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Network and other time-consuming operations cannot be carried out in the main thread, so a new thread is opened for network data transmission
                new Thread(){
                    @Override
                    public void run() {
                        new Client();
                        filename.setText("file("+Client.name+")Received");
                    }
                }.start();
            }
        });
        server.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Server(file).start();
            }
        });
    }
}

Server:

public class Server extends Thread{
    private Socket socket;
    private ServerSocket server;
    private FileInputStream fis;
    private DataOutputStream dos;
    private File file;
    private byte[] bytes;

    Server(File file){
        this.file=file;
    }

    public void run() {
        try {
            server = new ServerSocket(9999);
            while (true) {
                socket = server.accept();
                MainActivity.progress.setText("Sending");
                dos = new DataOutputStream(socket.getOutputStream());
                fis = new FileInputStream(file);
                bytes = new byte[(int)file.length()];
                //Transfer file name
                dos.writeUTF(file.getName());
                dos.flush();
                //Transfer file length
                dos.writeLong((int)file.length());
                dos.flush();
                int len=-1;
                // Read file into byte array
                while ((len = fis.read(bytes)) != -1) {
                    // Output byte array
                    dos.write(bytes);
                }
                dos.flush();
                fis.close();
                dos.close();
                socket.close();
                MainActivity.progress.setText(100 + "%");
                MainActivity.filename.setText("file("+file.getName()+")Send complete");
            }

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

Client:

public class Client{
    private DataOutputStream dos;
    private DataInputStream dis;
    private Socket socket;
    static String name;

    Client(){
        try {
            //ip address of server mobile phone
            socket = new Socket("192.168.2.170", 9999);
            dos = new DataOutputStream(socket.getOutputStream());
            dis = new DataInputStream(socket.getInputStream());
            name = dis.readUTF();// File name read
            long lengths = dis.readLong();
            byte[] bt = new byte[(int) lengths];
            long p = 0;
            for (int i = 0; i < bt.length; i = i + 2 * 1024 * 1024, p = p + 2 * 1024 * 1024) {
                //Each time 2 * 1024 * 1024 bytes of data are received, when the last less than 2 * 1024 * 1024 bytes are received, all the remaining data will be received
                if (i + 2 * 1024 * 1024 > bt.length) {
                    dis.readFully(bt, i, bt.length - i);
                    //Display the completion of the transmission
                    MainActivity.progress.setText(String.valueOf(p * 100 / bt.length) + "%");
                    System.out.println(p * 100 / bt.length + "%");
                } else {
                    dis.readFully(bt, i, 2 * 1024 * 1024);
                    //Display the completion of the transmission
                    MainActivity.progress.setText(String.valueOf(p * 100 / bt.length) + "%");
                    System.out.println(p * 100 / bt.length + "%");
                }
            }
            //Receive sent files
            File file;
            if (MainActivity.vis > 9) {
                file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MySocket/" + name);
            } else {
                file = new File("sdcard//" + "MySocket/" + name);
            }
            //Create folder or file if it does not exist
            if (!file.exists()) {
                file.getParentFile().mkdirs();
                file.createNewFile();
            }
            //Write the data in the byte array to the locally created file
            FileOutputStream fops = new FileOutputStream(file.getAbsoluteFile());
            fops.write(bt);
            fops.flush();
            fops.close();
            dis.close();
            dos.close();
            socket.close();
            System.out.println("File received.");
            MainActivity.progress.setText( "100%");
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}

xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/text_file"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:layout_above="@+id/btn_server"/>

    <TextView
        android:id="@+id/text_progress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="30sp"
        android:text="0%"
        android:gravity="center"
        android:layout_centerInParent="true"/>



    <Button
        android:id="@+id/btn_server"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text="send files"
        android:layout_centerHorizontal="true"
        android:layout_above="@+id/btn_client"/>

    <Button
        android:id="@+id/btn_client"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:text="receive files"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="100dp"/>

</RelativeLayout>

Points to note:
1.socket communication can only be under the same subnet, that is, two mobile phones must be connected to the same WIFI. It cannot be said that one mobile phone is connected to WIFI and one mobile phone uses traffic.
2. The permissions related to file reading and networking must be added. In addition to the permissions, don't forget to add them in the mainifests file
android:requestLegacyExternalStorage="true"


Then the source code will be put

Tags: Java Android socket

Posted on Thu, 02 Sep 2021 20:29:13 -0400 by n8w