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/155Tcp长连接
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() {
public void onResponse(Call call, Response response) throws IOException {
//返回服务器内容
String responsedata = response.body().string();
LogUtil.d("okHttp", responsedata);
}
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() {
public MediaType contentType() {
return MEDIA_TYPE_MARKDOWN;
}
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
166public 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{
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
8String value =
" {" +
" \"method\":\"update\"," +
" \"gatewayNo\":\"01\"," +
" \"userkey\":\"你的userkey\"" +
" }&^!";
//发送数据
tcpSocketHelper.sendmessage(value);处理数据 在service的onRecived中
本地处理完毕后,向服务器返回被控制器状态
1
2
3String value5 = "{\"method\":\"response\",\"result\":{\"successful\":true,\"message\":\"ok!\",\"data\":[{\"id\":\"D1\",\"value \":\"1\"}]}}&^! ";
tcpSocketHelper.sendmessage(value6);