Easy Tutorial
❮ Verilog Statements Block Git Gui Window ❯

7.6.3 Socket Communication Based on TCP Protocol (2)

Category Android Basic Tutorial

Introduction to This Section:

>

In the previous section, we introduced some basic concepts of Socket and how to use it, and then we wrote a simple chat room demo for a pig. I believe everyone has a preliminary grasp of Socket. In this section, we will learn how to use Socket to implement the resumption of large file transfers from the breakpoint! What is explained here is an example of someone else's well-written Socket to upload large files, and it is not required for us to be able to write it ourselves. It's just good to use it when needed!


1. Running Effect Picture:

  1. First, run our well-written Socket server:

  2. Put an audio file in the root directory of the SD card:

  3. Run our client:

  4. After the upload is successful, you can see a folder named file generated in the project of our server, where we can find the uploaded file: The .log file is our log file


2. Implementation Flowchart:



3. Code Example:

First, write a stream parsing class that both the server and client will use:

StreamTool.java :

public class StreamTool {

    public static void save(File file, byte[] data) throws Exception {
        FileOutputStream outStream = new FileOutputStream(file);
        outStream.write(data);
        outStream.close();
    }

    public static String readLine(PushbackInputStream in) throws IOException {
        char buf[] = new char[128];
        int room = buf.length;
        int offset = 0;
        int c;
        loop: while (true) {
            switch (c = in.read()) {
                case -1:
                case '\n':
                    break loop;
                case '\r':
                    int c2 = in.read();
                    if ((c2 != '\n') && (c2 != -1)) in.unread(c2);
                    break loop;
                default:
                    if (--room < 0) {
                        char[] lineBuffer = buf;
                        buf = new char[offset + 128];
                        room = buf.length - offset - 1;
                        System.arraycopy(lineBuffer, 0, buf, 0, offset);
                    }
                    buf[offset++] = (char) c;
                    break;
            }
        }
        if ((c == -1) && (offset == 0)) return null;
        return String.copyValueOf(buf, 0, offset);
    }

    /**
     * Read the stream
     * @param inStream
     * @return byte array
     * @throws Exception
     */
    public static byte[] readStream(InputStream inStream) throws Exception{
        ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        while( (len=inStream.read(buffer)) != -1){
            outSteam.write(buffer, 0, len);
        }
        outSteam.close();
        inStream.close();
        return outSteam.toByteArray();
    }
}

1) Implementation of the server:

Socket management and multi-thread management class:

FileServer.java :

public class FileServer {  

    private ExecutorService executorService; //Thread pool  
    private int port; //Listening port  
    private boolean quit = false; //Exit  
    private ServerSocket server;  
    private Map&lt;Long, FileLog> datas = new HashMap&lt;Long, FileLog>(); //Store breakpoint data  

    public FileServer(int port){  
        this.port = port;  
        //Create a thread pool, with (number of CPUs * 50) threads in the pool  
        executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50);  
    }  
    /** 
     * Exit 
     */  
    public void quit(){  
       this.quit = true;  
       try {  
           server.close();  
       } catch (IOException e) {  
       }  
    }  
    /** 
     * Start the service 
     * @throws Exception 
     */  
    public void start() throws Exception{  
        server = new ServerSocket(port);  
        while(!quit){  
            try {  
              Socket socket = server.accept();  
              //To support concurrent access by multiple users, a thread pool is used to manage each user's connection request  
              executorService.execute(new SocketTask(socket));  
            } catch (Exception e) {  
              //  e.printStackTrace();  
            }  
        }  
    }  

    private final class SocketTask implements Runnable{  
       private Socket socket = null;  
       public SocketTask(Socket socket) {  
           this.socket = socket;  
       }  


If the directory does not exist, create it;
file = new File(dir, filename);
If the file already exists (in case of a file name conflict during upload, then rename it)
filename = filename.substring(0, filename.indexOf(".") - 1) + dir.listFiles().length + filename.substring(filename.indexOf("."));
file = new File(dir, filename);
save(id, file);
} else { // If there is an upload record, read the length of the already uploaded data
file = new File(log.getPath()); // Get the file path from the upload record
if (file.exists()) {
File logFile = new File(file.getParentFile(), file.getName() + ".log");
if (logFile.exists()) {
Properties properties = new Properties();
properties.load(new FileInputStream(logFile));
position = Integer.valueOf(properties.getProperty("length")); // Read the length of the already uploaded data
}
}
}

OutputStream outStream = socket.getOutputStream();
String response = "sourceid=" + id + ";position=" + position + "\r\n";
// After the server receives the client's request information, it returns a response to the client: sourceid=1274773833264;position=0
// The sourceid is generated by the server side and uniquely identifies the uploaded file, and position indicates the position in the file where the client starts uploading
outStream.write(response.getBytes());

RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
if (position == 0) fileOutStream.setLength(Integer.valueOf(filelength)); // Set file length
fileOutStream.seek(position); // Specify the position in the file to start writing data
byte[] buffer = new byte[1024];
int len = -1;
int length = position;
while ((len = inStream.read(buffer)) != -1) { // Read data from the input stream and write it to the file
fileOutStream.write(buffer, 0, len);
length += len;
Properties properties = new Properties();
properties.put("length", String.valueOf(length));
FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName() + ".log"));
properties.store(logFile, null); // Record the received file length in real time
logFile.close();
}
if (length == fileOutStream.length()) delete(id);
fileOutStream.close();
inStream.close();
outStream.close();
file = null;

}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (socket != null && !socket.isClosed())
socket.close();
} catch (IOException e) {
}
}

}

public FileLog find(Long sourceid) {
return datas.get(sourceid);
}

// Save upload record
public void save(Long id, File saveFile) {
// Can be changed to store through the database in the future
datas.put(id, new FileLog(id, saveFile.getAbsolutePath()));
}

// When the file upload is complete, delete the record
public void delete(long sourceid) {
if (datas.containsKey(sourceid))
datas.remove(sourceid);
}

private class FileLog {
private Long id;
private String path;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public FileLog(Long id, String path) {
this.id = id;
this.path = path;
}
}

}

Server interface class: **ServerWindow.java**:


```java
public class ServerWindow extends Frame {
    private FileServer s = new FileServer(12345);
    private Label label;

    public ServerWindow(String title) {
        super(title);
        label = new Label();
        add(label, BorderLayout.PAGE_START);
        label.setText("Server has started");
        this.addWindowListener(new WindowListener() {
            public void windowOpened(WindowEvent e) {
                new Thread(new Runnable() {
                    public void run() {
                        try {
                            s.start();
                        } catch (Exception e) {
                            // e.printStackTrace();
                        }
                    }
                }).start();
            }

            public void windowIconified(WindowEvent e) {
            }

            public void windowDeiconified(WindowEvent e) {
            }

            public void windowDeactivated(WindowEvent e) {
            }

            public void windowClosing(WindowEvent e) {
                s.quit();
                System.exit(0);
            }
        });
    }
}

}

public void windowClosed(WindowEvent e) { }

public void windowActivated(WindowEvent e) { }; });

/**

}

}


2) Client (Android side)

Firstly, the layout file: activity_main.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="5dp">

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="File Name"
        android:textSize="18sp" />

    <EditText
        android:id="@+id/edit_fname"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Nikki Jamal - Priceless.mp3" />

    <Button
        android:id="@+id/btn_upload"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Upload" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Stop" />

    <ProgressBar
        android:id="@+id/pgbar"
        style="@android:style/Widget.ProgressBar.Horizontal"
        android:layout_width="fill_parent"
        android:layout_height="40px" />

    <TextView
        android:id="@+id/txt_result"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center" />
</LinearLayout>

For resumable uploads, we need to save the upload progress, which requires a database. Here we define a database management class: DBOpenHelper.java :

/**
 * Created by Jay on 2015/9/17 0017.
 */
public class DBOpenHelper extends SQLiteOpenHelper {

    public DBOpenHelper(Context context) {
        super(context, "jay.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS uploadlog (_id integer primary key autoincrement, path varchar(20), sourceid varchar(20))");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

Then there is the database operation class: UploadHelper.java :

/**
 * Created by Jay on 2015/9/17 0017.
 */
public class UploadHelper {
    private DBOpenHelper dbOpenHelper;

    public UploadHelper(Context context) {
        dbOpenHelper = new DBOpenHelper(context);
    }

    public String getBindId(File file) {
        SQLiteDatabase db = dbOpenHelper.getReadableDatabase();
        Cursor cursor = db.rawQuery("select sourceid from uploadlog where path=?", new String[]{file.getAbsolutePath()});
        if (cursor.moveToFirst()) {
            return cursor.getString(0);
        }
        return null;
    }

    public void save(String sourceid, File file) {
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        db.execSQL("insert into uploadlog(path,sourceid) values(?,?)",
                new Object[]{file.getAbsolutePath(), sourceid});
    }

    public void delete(File file) {
        SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
        db.execSQL("delete from uploadlog where path=?", new Object[]{file.getAbsolutePath()});
    }
}

Also, don't forget to include the stream parsing class on the client side. Finally, here is our MainActivity.java :

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText edit_fname;
    private Button btn_upload;
    private Button btn_stop;
    private ProgressBar pgbar;
    private TextView txt_result;

    private UploadHelper upHelper;
    private boolean flag = true;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            pgbar.setProgress(msg.getData().getInt("length"));
            float num = (float) pgbar.getProgress() / (float) pgbar.getMax();
            int result = (int) (num * 100);
            txt_result.setText(result + "%");
            if (pgbar.getProgress() == pgbar.getMax()) {
                Toast.makeText(MainActivity.this, "Upload successful", Toast.LENGTH_SHORT).show();
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

case R.id.btn_upload:
                String filename = edit_fname.getText().toString();
                flag = true;
                if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                    File file = new File(Environment.getExternalStorageDirectory(), filename);
                    if (file.exists()) {
                        pgbar.setMax((int) file.length());
                        uploadFile(file);
                    } else {
                        Toast.makeText(MainActivity.this, "The file does not exist~", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(MainActivity.this, "The SD card does not exist or is not available", Toast.LENGTH_SHORT).show();
                }
                break;
            case R.id.btn_stop:
                flag = false;
                break;
        }
    }

    private void uploadFile(final File file) {
        new Thread(new Runnable() {
            public void run() {
                try {
                    String sourceid = upHelper.getBindId(file);
                    Socket socket = new Socket("172.16.2.54", 12345);
                    OutputStream outStream = socket.getOutputStream();
                    String head = "Content-Length=" + file.length() + ";filename=" + file.getName()
                            + ";sourceid=" + (sourceid != null ? sourceid : "") + "\r\n";
                    outStream.write(head.getBytes());

                    PushbackInputStream inStream = new PushbackInputStream(socket.getInputStream());
                    String response = StreamTool.readLine(inStream);
                    String[] items = response.split(";");
                    String responseSourceid = items[0].substring(items[0].indexOf("=") + 1);
                    String position = items[1].substring(items[1].indexOf("=") + 1);
                    if (sourceid == null) { //If it is the first time to upload the file, there is no resource id bound to this file in the database
                        upHelper.save(responseSourceid, file);
                    }
                    RandomAccessFile fileOutStream = new RandomAccessFile(file, "r");
                    fileOutStream.seek(Integer.valueOf(position));
                    byte[] buffer = new byte[1024];
                    int len = -1;
                    int length = Integer.valueOf(position);
                    while (flag && (len = fileOutStream.read(buffer)) != -1) {
                        outStream.write(buffer, 0, len);
                        length += len; //Accumulate the length of the data that has been uploaded
                        Message msg = new Message();
                        msg.getData().putInt("length", length);
                        handler.sendMessage(msg);
                    }
                    if (length == file.length()) upHelper.delete(file);
                    fileOutStream.close();
                    outStream.close();
                    inStream.close();
                    socket.close();
                } catch (Exception e) {
                    Toast.makeText(MainActivity.this, "Upload exception~", Toast.LENGTH_SHORT).show();
                }
            }
        }).start();
    }

}

Finally, remember to write these permissions into AndroidManifest.xml!

<!-- Permission to create and delete files on the SD card -->
&lt;uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- Permission to write data to the SD card -->
&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- Permission to access the internet -->
&lt;uses-permission android:name="android.permission.INTERNET"/>

4. Code Download:

Socket Upload Large File Demo


5. Summary of This Section:

>

This section introduced another example of Socket based on the TCP protocol: using Socket to complete the resumption of large file uploads. I believe everyone's understanding of Socket has further improved. Well, let's write another example in the next section, an example of two mobile phones under the same Wifi transferring data to each other! That's all for now, thank you~

-1.0 Android Basic Tutorial

-1.0.1 Latest Android Basic Tutorial Catalog for 2015

-1.1 Background and System Architecture Analysis

-1.2 Development Environment Setup

-1.2.1 Developing Android APP with Eclipse + ADT + SDK

-1.2.2 Developing Android APP with Android Studio

-1.3 Solving SDK Update Issues

-1.4 Genymotion Emulator Installation

-1.5.1 Git Tutorial on Basic Operations of Local Repositories

-1.5.2 Git: Setting Up a Remote Repository with GitHub

-

-

❮ Verilog Statements Block Git Gui Window ❯