Android笔记—乐联网上传数据 TCP长连接总结


资料来源如下

  • 乐联网

编程环境

  • Android Studio 2.2.3

最终效果

  • 使用okhttp上传数据。
  • Tcp长连接实现方向控制
  • 以代码为主

相关教程

  • okhttp入门

    http://blog.csdn.net/biezhihua/article/details/50603624

  • 乐联网

    https://www.lewei50.com/dev/doc/176
    https://www.lewei50.com/dev/doc/155

  • Tcp长连接

    http://ls15114843569.blog.51cto.com/11015399/1767195

  • 简易上传

    http://ls15114843569.blog.51cto.com/11015399/1767195

上传数据

  • API介绍

    https://www.lewei50.com/dev/apiinfo/3

  • API测试

    https://www.lewei50.com/dev/apitest/3

  • 地址:http://www.lewei50.com/api/v1/gateway/updatesensors/你的网关号

    POST方式

    需要配置header头 Userkey

  • 数据发送/返回方式JSON

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [
    {
    "Name":"T1",
    "Value":"1"
    },
    {
    "Name":"01H1",
    "Value":"96.2"
    }
    ]

    返回格式

    1
    2
    3
    4
    {
    "Successful": true,
    "Message": null
    }
  • okhttp POST 传感器数据 这里使用了一个静态内部类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    	//返回数据处理
    public okhttp3.Callback callback = new okhttp3.Callback() {
    @Override
    public void onResponse(Call call, Response response) throws IOException {
    //返回服务器内容
    String responsedata = response.body().string();
    LogUtil.d("okHttp", responsedata);
    }

    @Override
    public void onFailure(Call call, IOException e) {
    //异常处理
    LogUtil.d("okHttp", "POST错误");
    }
    };

    //内部类
    public static class Http {
    public static final MediaType MEDIA_TYPE_MARKDOWN
    = MediaType.parse("text/x-markdown; charset=utf-8");
    //POST数据,指定接收回掉
    public static void postData(String sensor_name, String sensor_data, okhttp3.Callback callback) {
    OkHttpClient client = new OkHttpClient();
    final String value =
    "[" +
    " {" +
    " \"Name\":\"" + sensor_name + "\"," +
    " \"Value\":\"" + sensor_data + "\"" +
    " }" +
    "]";
    RequestBody requestBody = new RequestBody() {
    @Override
    public MediaType contentType() {
    return MEDIA_TYPE_MARKDOWN;
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
    sink.write(value.getBytes());
    }
    };
    Request request = new Request.Builder()
    .url("http://www.lewei50.com/api/V1/gateway/UpdateSensors/01")
    .header("userkey", "你的userkey")
    .post(requestBody)
    .build();
    client.newCall(request).enqueue(callback);
    }
    }
  • 实际使用 放在一个后台服务内,调用相当简单

    1
    Http.postData("PM2.5", "你的数据转为String", callback);

    Tcp长连接,远程控制

  • 首先参考乐联网反向控制教程,新建一个控制器,这里以开关为例。

  • 原理是与服务器保持一个TCP长连接,不到40s刷新一次,以此保持通道,与被控制段通信,发送控制信息。

  • Tcp长连接参考了@墨迹流韶的Android基于Tcp协议的Socket长链接封装

  • 地址 tcp.lewei50.com
    端口号 9960
    心跳包间隔 1min以内

  • 发送/接收数据格式 Json
    本地发送数据格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    {

    "method": "update",

    "gatewayNo": "你的网关号",

    "userkey": "你的userkey"

    }&^!

    服务器发送控制命令格式,数据处理时需要去掉字符串最后的&^!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16

    {

    "method":"send",

    "gatewayNo":"01",

    "userkey":"6d16ddb3c58c4e448a7e15e7acxxxxxx",

    "f":" updateSensor",

    "p1":"D1",

    "p2":"1"

    }&^!

    本地响应控制命令后返回数据格式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    {

    "method":"response",

    "result":{

    "successful":true,

    "message":"ok!",

    "data":[{

    "id":"D1",

    "value":"1"

    },

    }&^!
  • TCP连接类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    public class TcpSocketHelper {
    private String mTag = "TcpSocketHelper";
    private Socket socket;
    private boolean _connect;
    private ReceiveThread mReceiveThread;
    private boolean receiveStop;
    private Date lastKeepAliveOkTime;
    private OnRecivedListener mRecivedListener;
    //地址
    private String mIpAddr = "http://tcp.lewei50.com";
    //端口
    private int mPort = 9960;

    /**
    * 开启链接socket
    * @param ipAddr
    * @param port
    */
    public void startConnect(String ipAddr, int port){
    LogUtil.d(mTag, "准备链接...");
    this.mIpAddr = ipAddr;
    this.mPort = port;
    InetAddress serverAddr;
    try {
    socket = new Socket(ipAddr, port);
    LogUtil.d(mTag, "准备链接...");
    _connect = true;
    mReceiveThread = new ReceiveThread();
    receiveStop = false;
    mReceiveThread.start();
    LogUtil.d(mTag, "链接成功...");

    } catch (Exception e) {
    LogUtil.d(mTag, "链接出错..." + e.getMessage());
    e.printStackTrace();
    }
    }

    /**
    * 关闭链接
    */
    public void closeConnect(){
    if (socket != null){
    try {
    socket.close();
    socket = null;
    } catch (IOException e) {
    e.printStackTrace();
    }
    }
    }

    /**
    * 保持心跳
    */
    public void KeepAlive() {
    // 判断socket是否已断开,断开就重连
    if (lastKeepAliveOkTime != null) {
    LogUtil.d(mTag, "上次心跳成功时间:"+ DateFormat.format("yyyy-MM-dd HH:mm:ss", lastKeepAliveOkTime));
    Date now = Calendar.getInstance().getTime();
    long between = (now.getTime() - lastKeepAliveOkTime.getTime());// 得到两者的毫秒数
    if (between > 60 * 1000) {
    LogUtil.d(mTag, "心跳异常超过40,重新连接:");
    lastKeepAliveOkTime = null;
    socket = null;
    }

    } else {
    lastKeepAliveOkTime = Calendar.getInstance().getTime();
    }

    if (!checkIsAlive()) {
    LogUtil.d(mTag, "链接已断开,重新连接.");
    startConnect(mIpAddr, mPort);
    }
    }

    /**
    * 此方法是检测是否连接
    * @return
    */
    public boolean checkIsAlive() {
    if (socket == null||!socket.isConnected())
    return false;
    return true;

    }

    /**
    * 发送数据的方法
    * @param msg
    */
    public void sendmessage(String msg) {
    boolean isAlive = checkIsAlive();
    if (!isAlive)
    return;
    LogUtil.d(mTag, "准备发送消息:" + msg);
    try {
    if (socket != null && socket.isConnected()) {
    if (!socket.isOutputShutdown()) {

    //2.得到socket读写流
    OutputStream os=socket.getOutputStream();
    //true:是否自动flush
    PrintWriter outStream=new PrintWriter(os, true);
    outStream.print(msg);
    outStream.flush();
    }
    }
    LogUtil.d(mTag, "发送成功!");
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * 设置接收数据监听器
    * @param mRecivedListener
    */
    public void setmRecivedListener(OnRecivedListener mRecivedListener) {
    this.mRecivedListener = mRecivedListener;
    }

    /**
    * 数据接收线程
    */
    class ReceiveThread extends Thread{

    @Override
    public void run() {
    while (true) {
    try {
    sleep(2000);
    // 判断 Socket 是否处于连接状态
    if (socket != null && socket.isConnected()) {
    // 客户端接收服务器端的响应,读取服务器端向客户端的输入流
    InputStream isRead = socket.getInputStream();
    // 缓冲区
    byte[] buffer = new byte[isRead.available()];
    // 读取缓冲区
    isRead.read(buffer);
    // 转换为字符串
    String responseInfo = new String(buffer);
    // 日志中输出
    if(responseInfo != null&&!responseInfo.equals("")){
    LogUtil.d("TcpManager", "返回:"+responseInfo);
    mRecivedListener.onRecived(responseInfo);
    }

    lastKeepAliveOkTime = Calendar.getInstance().getTime();
    KeepAlive();

    continue;
    } else {
    if (socket != null)
    LogUtil.d(mTag, "链接状态:" + socket.isConnected());
    }

    } catch (Exception e) {
    LogUtil.d(mTag, "监听出错:" + e.toString());
    e.printStackTrace();
    }
    }
    }
    }
    }
  • 使用,包装在一个后台service中,在service中实现TcpSocketHelper的onRecived方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
      //tcp返回信息
    @Override
    public void onRecived(String data) {
    LogUtil.d("okHttpService", data);
    //处理服务器发回的数据
    }

    TcpSocketHelper tcpSocketHelper = new TcpSocketHelper();
    tcpSocketHelper.startConnect("tcp.lewei50.com", 9960);
    //设置监听
    tcpSocketHelper.setmRecivedListener(this);
  • 发送心跳包

    1
    2
    3
    4
    5
    6
    7
    8
    String value =
    " {" +
    " \"method\":\"update\"," +
    " \"gatewayNo\":\"01\"," +
    " \"userkey\":\"你的userkey\"" +
    " }&^!";
    //发送数据
    tcpSocketHelper.sendmessage(value);
  • 处理数据 在service的onRecived中

  • 本地处理完毕后,向服务器返回被控制器状态

    1
    2
    3
    String value5 = "{\"method\":\"response\",\"result\":{\"successful\":true,\"message\":\"ok!\",\"data\":[{\"id\":\"D1\",\"value \":\"1\"}]}}&^! ";

    tcpSocketHelper.sendmessage(value6);