通过TCP实现OTA的点对点上传,实现进度的监听

通过TCP实现OTA的点对点上传,实现进度的监听

其实这个功能时比较简单的,但是有点逻辑性,也是工作中碰到的,本文只讲概念代码,并不会去具体实现,不过我相信你看完之后会知道如何去做的,相信我

假设我现在一个手机端,一个设备端,而我们现在做的是手机端,设备端我们暂时不管,那我们通过OTA是怎么样的流程?

  • 1.检测内存卡上是否存在update.zip
  • 2.进行加密效验(比如MD5,CRC32)
  • 3.TCP(端口8080)发送CMD_UPDATE_LODING:校验码
  • 4.设备端回应CMD_UPDATE_LODING:0 | -1
  • 5.TCP(端口9090)发送文件
  • 6.设备端返回CMD_ACK_OTA_Upload_Success | CMD_ACK_OTA_Upload_Fault
  • 7.TCP(端口8080)发送CMD_OTA_Update

这就是一个比较完整的看逻辑了,那好,我们就用伪代码来实现一遍,首先是创建8080的TCP端口连接

    /** * TCP连接 */
    private void connect() {
        Log.i(TAG, "connect");
        new Thread() {
            @Override
            public void run() {
                try {
                    mSocket = new Socket("ip", 8080);
                    mReader = mSocket.getInputStream();
                    mWriter = mSocket.getOutputStream();
                    revMsg();
                    mWriter.flush();
                } catch (IOException e) {
                    Log.e(TAG, "connect:" + e.toString());
                }
            }
        }.start();
    }

这里为了发送和接收方便,我们单独封装两个个方法

 /** * 发送指令 * * @param msg */
    private void sendMsg(final String msg) {
        new Thread() {
            @Override
            public void run() {
                try {
                    Log.i(TAG, "send:" + msg);
                    mWriter.write(msg.getBytes("utf-8"));
                    mWriter.flush();
                } catch (Exception e) {
                    Log.e(TAG, "sendMsg:" + e.toString());
                }
            }
        }.start();
    }

    /** * 接收 */
    private void revMsg() {
        new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        byte[] mbyte = new byte[1024];
                        int temp = mReader.read(mbyte);
                        String result = new String(mbyte, 0, temp);
                        Log.i(TAG, "result:" + result);
                    }
                } catch (Exception e) {
                    Log.e(TAG, "revMsg:" + e.toString());
                }
            }
        }.start();
    }

专门用来发送和接收,并且我们实现一个MD5Utils

public class MD5Utils {

    /** * MD5加密文件 * @param file * @return * @throws FileNotFoundException */
    public static String getMd5ByFile(File file) throws FileNotFoundException {
        String value = null;
        FileInputStream in = new FileInputStream(file);
        try {
            MappedByteBuffer byteBuffer = in.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length());
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(byteBuffer);
            BigInteger bi = new BigInteger(1, md5.digest());
            value = bi.toString(16);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != in) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return value;
    }
}

OK,前面的工作都做完了,那我们就来实现逻辑了,在点击事件里检测了

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnOta:
                //1.检查是否存在update.zip
                File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/update.zip");
                if (file.exists()) {

                } else {
                    Toast.makeText(this, "未检测到固件", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

可以,在这里我们把第一步实现了,然后第二步如果不耗时的话可以和第三步一起,我之前就碰到一个15MB的File进行CRC32的时候就耗时了,所以假设不耗时,我们可以直接发送CMD_UPDATE_LODING:校验码,如代码

  @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnOta:
                //1.检查是否存在update.zip
                File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/update.zip");
                if (file.exists()) {
                    try {
                        sendMsg("CMD_UPDATE_LODING:" + MD5Utils.getMd5ByFile(file));
                    } catch (FileNotFoundException e) {
                        Log.e(TAG, "MD5 Error" + e.toString());
                    }
                } else {
                    Toast.makeText(this, "未检测到固件", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }

这里算是发出去了,那我们在接收的地方可以这样去操作

   /** * 接收 */
    private void revMsg() {
        new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        byte[] mbyte = new byte[1024];
                        int temp = mReader.read(mbyte);
                        String result = new String(mbyte, 0, temp);
                        Log.i(TAG, "result:" + result);
                        if (result.startsWith("CMD_UPDATE_LODING")) {
                            String[] mStr = result.split(":");
                            switch (mStr[1]) {
                                case "0":
                                    mHandler.sendEmptyMessage(UPDATE_OK);
                                    break;
                                case "-1":
                                    mHandler.sendEmptyMessage(UPDATE_OK);
                                    break;
                            }
                        }
                    }
                } catch (Exception e) {
                    Log.e(TAG, "revMsg:" + e.toString());
                }
            }
        }.start();
    }

这里如果他返回0,说明他准备好了

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case UPDATE_OK:
                    connectFile();
                    break;
                case UPDATE_NO:
                    Toast.makeText(MainActivity.this, "OTA未准备好", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

是0,那我就可以开始推送File了,推送File要要考虑的就是进度,所以我专门写了一个方法来监听进度

   /** * 上传文件 */
    private void connectFile() {
        new Thread() {
            @Override
            public void run() {
                try {
                    Log.i(TAG, "connectFile");
                    mSocketFile = new Socket("ip", 9090);
                    mReaderFile = mSocketFile.getInputStream();
                    mWriterFile = mSocketFile.getOutputStream();
                    File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/update.zip");
                    FileInputStream fin = new FileInputStream(file);
                    OutputStream out = mSocketFile.getOutputStream();
                    long fileLength = file.length();
                    byte[] buf = new byte[2048];
                    int len = 0;
                    int progress = 0;
                    int progressAll = 0;
                    while ((len = fin.read(buf)) != -1) {
                        out.write(buf, 0, len);
                        progress += len;
                        progressAll = progress * 100 / (int) fileLength;
                        System.out.println("progressAll"+ progressAll);
                    }
                    mSocketFile.shutdownInput();
                    mWriterFile.flush();
                    fin.close();
                    mSocketFile.close();
                } catch (IOException e) {
                    Log.e(TAG, "connectFile:" + e.toString());
                }
            }
        }.start();
    }

这个方法要仔细看,我在这里新开了一个端口9090,然后进行文件的上传,其中我还在计算当前的进度为 progress += len,去乘以100/总大小fileLength就是我们当前的进度了,OK,这个时候他返回的是什么呢?这个时候设备端返回CMD_ACK_OTA_Upload_Success | CMD_ACK_OTA_Upload_Fault,我们又回到了接收的地方

 } else if (result.equals("CMD_ACK_OTA_Upload_Success")) {
      mHandler.sendEmptyMessage(UPLOAD_SUCCESS);
 } else if (result.equals("CMD_ACK_OTA_Upload_Fault")) {
      mHandler.sendEmptyMessage(UPLOAD_FAULT);
 }

我在接收的地方判断了结果,然后继续回到handler里面

case UPLOAD_SUCCESS:
     sendMsg("CMD_OTA_Update");
     break;
case UPLOAD_FAULT:
     Toast.makeText(MainActivity.this, "文件效验失败", Toast.LENGTH_SHORT).show();
     break;

这里更容易,如果成功我就发送CMD_OTA_Update,失败我就提示失败,就是这么简单,到此流程就完全走了一遍了,虽然是伪代码,不过逻辑还是痛顺畅的,送上完整的伪代码

package com.liuguilin.ota_tcp_sample;

import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/** * - 1.检测内存卡上是否存在update.zip * - 2.进行加密效验(比如MD5,CRC32) * - 3.TCP(端口8080)发送CMD_UPDATE_LODING:校验码 * - 4.设备端回应CMD_UPDATE_LODING:0 | -1 * - 5.TCP(端口9090)发送文件 * - 6.设备端返回CMD_ACK_OTA_Upload_Success | CMD_ACK_OTA_Upload_Fault * - 7.TCP(端口8080)发送CMD_OTA_Update */
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static final String TAG = MainActivity.class.getSimpleName();

    public static final int UPDATE_OK = 1001;
    public static final int UPDATE_NO = 1002;

    public static final int UPLOAD_SUCCESS = 1003;
    public static final int UPLOAD_FAULT = 1004;

    //tcp 8080
    private Socket mSocket;
    private InputStream mReader;
    private OutputStream mWriter;

    //tcp 9090
    private Socket mSocketFile;
    private InputStream mReaderFile;
    private OutputStream mWriterFile;

    private Button btnOta;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case UPDATE_OK:
                    connectFile();
                    break;
                case UPDATE_NO:
                    Toast.makeText(MainActivity.this, "OTA未准备好", Toast.LENGTH_SHORT).show();
                    break;
                case UPLOAD_SUCCESS:
                    sendMsg("CMD_OTA_Update");
                    break;
                case UPLOAD_FAULT:
                    Toast.makeText(MainActivity.this, "文件效验失败", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

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

        //开始连接
        connect();
        btnOta = (Button) findViewById(R.id.btnOta);
        btnOta.setOnClickListener(this);
    }

    /** * TCP连接 */
    private void connect() {
        Log.i(TAG, "connect");
        new Thread() {
            @Override
            public void run() {
                try {
                    mSocket = new Socket("ip", 8080);
                    mReader = mSocket.getInputStream();
                    mWriter = mSocket.getOutputStream();
                    revMsg();
                    mWriter.flush();
                } catch (IOException e) {
                    Log.e(TAG, "connect:" + e.toString());
                }
            }
        }.start();
    }

    /** * 发送指令 * * @param msg */
    private void sendMsg(final String msg) {
        new Thread() {
            @Override
            public void run() {
                try {
                    Log.i(TAG, "send:" + msg);
                    mWriter.write(msg.getBytes("utf-8"));
                    mWriter.flush();
                } catch (Exception e) {
                    Log.e(TAG, "sendMsg:" + e.toString());
                }
            }
        }.start();
    }

    /** * 接收 */
    private void revMsg() {
        new Thread() {
            @Override
            public void run() {
                try {
                    while (true) {
                        byte[] mbyte = new byte[1024];
                        int temp = mReader.read(mbyte);
                        String result = new String(mbyte, 0, temp);
                        Log.i(TAG, "result:" + result);
                        if (result.startsWith("CMD_UPDATE_LODING")) {
                            String[] mStr = result.split(":");
                            switch (mStr[1]) {
                                case "0":
                                    mHandler.sendEmptyMessage(UPDATE_OK);
                                    break;
                                case "-1":
                                    mHandler.sendEmptyMessage(UPDATE_OK);
                                    break;
                            }
                        } else if (result.equals("CMD_ACK_OTA_Upload_Success")) {
                            mHandler.sendEmptyMessage(UPLOAD_SUCCESS);
                        } else if (result.equals("CMD_ACK_OTA_Upload_Fault")) {
                            mHandler.sendEmptyMessage(UPLOAD_FAULT);
                        }
                    }
                } catch (Exception e) {
                    Log.e(TAG, "revMsg:" + e.toString());
                }
            }
        }.start();
    }

    /** * 上传文件 */
    private void connectFile() {
        new Thread() {
            @Override
            public void run() {
                try {
                    Log.i(TAG, "connectFile");
                    mSocketFile = new Socket("ip", 9090);
                    mReaderFile = mSocketFile.getInputStream();
                    mWriterFile = mSocketFile.getOutputStream();
                    File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/update.zip");
                    FileInputStream fin = new FileInputStream(file);
                    OutputStream out = mSocketFile.getOutputStream();
                    long fileLength = file.length();
                    byte[] buf = new byte[2048];
                    int len = 0;
                    int progress = 0;
                    int progressAll = 0;
                    while ((len = fin.read(buf)) != -1) {
                        out.write(buf, 0, len);
                        progress += len;
                        progressAll = progress * 100 / (int) fileLength;
                        System.out.println("progressAll" + progressAll);
                    }
                    mSocketFile.shutdownInput();
                    mWriterFile.flush();
                    fin.close();
                    mSocketFile.close();
                } catch (IOException e) {
                    Log.e(TAG, "connectFile:" + e.toString());
                }
            }
        }.start();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnOta:
                //1.检查是否存在update.zip
                File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/update.zip");
                if (file.exists()) {
                    try {
                        sendMsg("CMD_UPDATE_LODING:" + MD5Utils.getMd5ByFile(file));
                    } catch (FileNotFoundException e) {
                        Log.e(TAG, "MD5 Error" + e.toString());
                    }
                } else {
                    Toast.makeText(this, "未检测到固件", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

有兴趣的可以加群:555974449

Sample下载地址:正在上传

你可能感兴趣的:(工作,tcp,内存,手机,OTA)