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

来源:互联网 发布:淘宝为什么比京东便宜 编辑:程序博客网 时间:2024/06/02 06:41

通过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下载地址:正在上传

1 0
原创粉丝点击