IOT设备SmartConfig实现
一般情况下,IOT设备(针对wifi设备)在智能化过程中需要连接到家庭路由。但在此之前,需要将wifi信息(通常是ssid和password,即名字和密码)发给设备,这一步骤被称为配网。移动设备如Android、iOS等扮演发送wifi信息的角色,简单来说就是移动应用要与IOT设备建立通信,进而交换数据。针对配网这一步骤,市面上一般有两种做法:
- AP连接方式:IOT设备发出AP(Access Point,可理解为路由器,可发出wifi)信息;移动设备STA(Station,可以连接wifi)连接到IOT设备AP,接着就可以发送wifi(家庭路由的wifi)信息给设备了。另外,也可互换角色,及移动设备释放热点,IOT设备进行连接。
- SmartConfig(一键配置)方式:不需要建立连接,移动设备将wifi信息(需提前获取)写入数据包,组播循环发出此数据包;IOT设备处于监听所有网络的模式,接收到UDP包后解析出wifi信息拿去连网。
可以发现,SmartConfig不需建立连接,步骤较少,实现起来也较容易,并且用户也无需进行过多的操作。本文的IOT设备基于ESP32开发板,解释原理及实现如何通过Android APP发出UDP
包实现SmartConfig。知识点:计算机网络、UDP
、组播、DatagramSocket
...
一、网络知识回顾
计算机网络分层结构如下:
- 应用层:体系中的最高层。任务是通过应用程序间的交互来完成特定网络应用。不同的网络应用对应不同的协议:如
HTTP
、DNS
、SMTP
。其交互的数据单元称为报文。 - 运输层:复杂向两台主机中进程直接的通信提供通用的数据传输服务,使用端口作为向上传递的进程标识,主要有TCP和UDP。
- 网络层:负责为分组交换网络上的不同主机提供通信服务,使用IP协议。
- 网络接口层:包括数据链路层和物理层,传输单位分别是帧和比特。
1. IP协议
IP(Internet Protocol)协议是网络层的主要协议。其版本有IPv4(32位)、IPv6(128位)。与IP协议配套使用的还有地址解析协议(ARP)、网际控制报文协议(ICMP,重要应用即常见的PING,测试连通性)、网际组管理协议(IGMP)。
IP数据报格式,由首部和数据部分两部分组成:
IP地址分类如下:
1.1 两级IP地址
IP地址是每一台主机唯一的标识符,由网络号和主机号组成。A、B、C三类均为单播地址(一对一),D类为多播地址(一对多)。
1.2 三级IP地址
在两级中新增了子网号字段,也称为划分子网。其方法是从主机号借用若干位作为子网号。
子网掩码:是一个网络或子网的重要属性,可通过子网掩码计算出目的主机所处于哪一个子网。若没有划分子网,则使用默认子网掩码(A类255.0.0.0、B类255.255.0.0、C类255.255.255.0)
1.3 无分类编址
无分类编址(CIDR)也称为构造超网,使用网络前缀+主机号规则,并使用斜线标明前缀位数,如:
128.15.34.77/20
1.4 IP多播
多播又称为组播,提供一对多的通信,大大节约网络资源。IP数据报中不能写入某一个IP地址,需写入多播组的标识符(需要接收的主机与此标识符关联)。D类地址即为多播组的标识符,所以多播地址(D类)只能作为目的地址。分为本局域网上的硬件多播、互联网多播两种。
多播使用到的协议:
- IGMP(网际组管理协议):让连接在本地局域网上的多播路由器(能够运行多播协议的路由器)知道本局域网上是否有主机(进程)参加或退出了某个多播组。
- 多播路由选择协议:用于多播路由器之间的协同工作,以便让多播数据报以最小的代价传输。
2. UDP协议
运输层向上面的应用层提供通信服务,通信的端点并不是主机而是主机中进程,使用协议端口号标识进程(如HTTP为80)。UDP协议是运输层中重要的两个协议之一。
- UDP是无连接的
- UDP使用尽最大努力交付,不保证可靠交付
- UDP是面向报文的
- UDP没有拥塞控制
- UDP支持一对一,一对多,多对一和多对多
- UDP首部开销小
二、Java中的UDP
1. Socket
socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。简单来说,socket是一种接口,对传输层(TCP/UPD协议)进行了的封装。
socket通信:
- TCP socket:需建立连接,TCP三次握手,基于流的通信(InputStrea和OutputStream)
- UDP socket:无需建立连接,基于报文的通信。可以组播的形式发出报文,适合本场景中的配网步骤。
2. Java中的socket
2.1 类解释
Java为Socket编程封装了几个重要的类(均为客户端-服务端模式):
Socket
类:
实现了一个客户端socket,作为两台机器通信的终端,默认采用TCP。connect()
方法请求socket连接、getXXXStream()
方法获取输入/出流、close()
关闭流。
ServerSocket
类:
实现了一个服务器的socket,等待客户端的连接请求。bind()
方法绑定一个IP
地址和端口、accept()
方法监听并返回一个Socket
对象(会阻塞)、close()
关闭一个socket
。
SocketAddress # InetSocketAddress
类:
前者是一个抽象类,提供了一个socket地址,不关心传输层协议;后者继承自前者,表示带有IP地址和端口号的socket地址。
DatagramSocket
类:
实现了一个发送和接收数据报的socket,使用UDP。send()
方法发送一个数据报(DatagramPacket
)、receive()
方法接收一个数据报(一直阻塞接至收到数据报或超时)、close()
方法关闭一个socket。
DatagramPacket
类:
使用DatagramSocket
时的数据报载体。
2.2 UDP实例
SmartConfig采用UDP实现,所以在前述知识的基础下,先编写一个例子熟悉java udp的使用,首先建立服务端的代码:
public class UDPServer {
/**
* 设置缓冲区的长度
*/
private static final int BUFFER_SIZE = 255;
/**
* 指定端口,客户端需保持一致
*/
private static final int PORT = 8089;
public static void main(String[] args) {
DatagramSocket datagramSocket = null;
try {
datagramSocket = new DatagramSocket(PORT);
DatagramPacket datagramPacket = new DatagramPacket(new byte[BUFFER_SIZE], BUFFER_SIZE);
while (true) {
// 接收数据报,处于阻塞状态
datagramSocket.receive(datagramPacket);
System.out.println("Receive data from client:" + new String(datagramPacket.getData()));
// 服务器端发出响应信息
byte[] responseData = "Server response".getBytes();
datagramPacket.setData(responseData);
datagramSocket.send(datagramPacket);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (datagramSocket != null) {
datagramSocket.close();
}
}
}
}
客户端发出数据报:
public class UDPClient {
/**
* 指定端口,与服务端保持一致
*/
private static final int PORT = 8089;
/**
* 超时重发时间
*/
private static final int TIME_OUT = 2000;
/**
* 最大重试次数
*/
private static final int MAX_RETRY = 3;
public static void main(String[] args) throws IOException {
try {
byte[] sendMsg = "Client msg".getBytes();
// 创建数据报
DatagramSocket socket = new DatagramSocket();
// 设置阻塞超时时间
socket.setSoTimeout(TIME_OUT);
// 创建server主机的ip地址(此处使用了本机地址)
InetAddress inetAddress = InetAddress.getByName("192.168.xxx.xxx");
// 发送和接收的数据报文
DatagramPacket sendPacket = new DatagramPacket(sendMsg, sendMsg.length, inetAddress, PORT);
DatagramPacket receivePacket = new DatagramPacket(new byte[sendMsg.length], sendMsg.length);
// 数据报文可能丢失,设置重试计数器
int tryTimes = 0;
boolean receiveResponse = false;
// 将数据报文发送出去
socket.send(sendPacket);
while (!receiveResponse && (tryTimes < MAX_RETRY)) {
try {
// 阻塞接收数据报文
socket.receive(receivePacket);
// 检查返回的数据报文
if (!receivePacket.getAddress().equals(inetAddress)) {
throw new IOException("Unknown server's data");
}
receiveResponse = true;
} catch (InterruptedIOException e) {
// 重试
tryTimes++;
System.out.println("TimeOut, try " + (MAX_RETRY - tryTimes) + " times");
}
}
if (receiveResponse) {
System.out.println("Receive from server:" + new String(receivePacket.getData()));
} else {
System.out.println("No data!");
}
socket.close();
} catch (SocketException e) {
e.printStackTrace();
}
}
}
运行结果:
* 发现客户端收到的数据被截断了,这是因为没有重置接收包的长度,在服务端datagramPacket.setLength()
可解决。
三、SmartConfig
根据前面的socket相关应用,基本想到如何实现一键配置。在实际应用中,原理一样,只是增加了组播(这一点需要和IOT设备端共同确定,数据的格式也需协定)。在实现中,需要针对不同IP组播地址发出循环的UDP报文,增加设备端接收到的可能性;同时APP也要开启服务端程序监听发出数据报的响应,以此更新UI或进行下一步的数据通信。相关核心代码如下:
// 对每一个组播地址循环发出报文
while (!mIsInterrupt && System.currentTimeMillis() - currentTime < mParameter
.getTimeoutGuideCodeMillisecond()) {
mSocketClient.sendData(gcBytes2,
mParameter.getTargetHostname(),
mParameter.getTargetPort(),
mParameter.getIntervalGuideCodeMillisecond());
// 跳出条件,发出UDP报文达到一定时间
if (System.currentTimeMillis() - startTime > mParameter.getWaitUdpSendingMillisecond()) {
break;
}
}
组播地址设置:
public String getTargetHostname() {
if (mBroadcast) {
return "255.255.255.255";
} else {
int count = __getNextDatagramCount();
return "234." + (count + 1) + "." + count + "." + count;
}
}
完整代码省略(利益相关,代码匿了^_^
),基本思路很简单。最终的实现是IOT设备收到UDP发出的wifi信息,并以此成功连接wifi,连接服务器,进而绑定账号。
IOT设备SmartConfig实现的更多相关文章
- Win10 IoT C#开发 5 - 操作 IoT 设备内嵌 SQLite 数据库 CURD
Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本不同,是为物联网设备专门设计的,硬件也不仅仅限于x86架构,同时可以在ARM架构上运行. 前几章我 ...
- 操作 IoT 设备内嵌 SQLite
Win10 IoT C#开发 5 - 操作 IoT 设备内嵌 SQLite 数据库 CURD Windows 10 IoT Core 是微软针对物联网市场的一个重要产品,与以往的Windows版本 ...
- 恶意软件Mirai换了个马甲 瞄上我国2亿多台IoT设备
恶意软件Mirai换了个马甲 瞄上我国2亿多台IoT设备 想要起来时,一种沉重感阻碍着他,这是一种安全感:感觉到一张床为他铺好了,而且只属于他:想要静卧时,一种不安阻碍着他,把他从床上赶起来,这是 ...
- 省钱版----查找 IoT 设备TTL线序__未完待续
作者:仙果 原文来自:省钱版—-查找 IoT 设备TTL线序 省钱版----查找 IoT 设备TTL线序__未完待续 缘由 在IoT固件调试分析的过程中,建议首先在IoT设备的板子上焊接调试线,这是能 ...
- Remaiten-一个以路由器和IoT设备为目标的Linux bot
Remaiten-一个以路由器和IoT设备为目标的Linux bot ESET的研究人员正在积极地检测以嵌入式系统为攻击目标的木马,受影响的有路由器,网关和无线访问点.近期,我们已经发现了一个相关的b ...
- 安天透过北美DDoS事件解读IoT设备安全——Mirai的主要感染对象是linux物联网设备,包括:路由器、网络摄像头、DVR设备,入侵主要通过telnet端口进行流行密码档暴力破解,或默认密码登陆,下载DDoS功能的bot,运行控制物联网设备
安天透过北美DDoS事件解读IoT设备安全 安天安全研究与应急处理中心(安天CERT)在北京时间10月22日下午启动高等级分析流程,针对美国东海岸DNS服务商Dyn遭遇DDoS攻击事件进行了跟进分析. ...
- IoT设备程序开发及编译环境搭建初体验
引言 Mirai事件一经曝出,立即引领了一轮研究IoT设备的热潮.目前,对Mirai的报告大多只是在对其功能实现上的介绍,却很少提及如何实现IoT设备程序开发的测试环境.本文在对Mirai的源码研究的 ...
- IOT设备的7大安全问题
IOT设备的7大安全问题 串口安全 IOT设备一般包含各类串口,并且这些串口缺乏认证机制.一旦暴露给了hacker,hacker可以很容易的查找敏感信息和dump固件,从而导致各类安全问题.建议厂家在 ...
- IoT设备上的恶意软件——通过漏洞、弱密码渗透
2018年,是 IoT 高速发展的一年,从空调到电灯,从打印机到智能电视,从路由器到监控摄像头统统都开始上网.随着5G网络的发展,我们身边的 IoT 设备会越来越多.与此同时,IoT 的安全问题也慢慢 ...
随机推荐
- raw_input和input
昨天在OJ上做CTF的题目,发现有道python的题目很有意思,让我知道了raw_input和input的区别,并且能干一些别的事情. 官方文档上说,input()相当于eval(raw_input( ...
- SpringBoot 之 拦截配置 与SpringCloud
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.SpringBoot 与 SpringMVC 的区别? SpringMVC是基于Spring的MV ...
- Java实现 LeetCode 804 唯一摩尔斯密码词 (暴力)
804. 唯一摩尔斯密码词 国际摩尔斯密码定义一种标准编码方式,将每个字母对应于一个由一系列点和短线组成的字符串, 比如: "a" 对应 ".-", " ...
- ASP.NET中LINQ的基本用法
此Demo只是一个极其简单的LINQ查询Demo 一个类 using System; using System.Collections.Generic; using System.Linq; usin ...
- ASP.NET中IHttpHandler与IHttpModule的区别(带样例说明)
IHttpModule相对来说,是一个网页的添加 IHttpHandler相对来说,却是网页的替换 先建一个HandlerDemo的类 using System; using System.Colle ...
- 双向链表都不懂,还说懂Redis?
目录 redis源码分析系列文章 前言 API使用 lpush左侧插入数据 rpush右侧插入数据 删除某个数据 修改某个数据 具体逻辑图 双向链表的定义 节点ListNode 整体架构 双向链表的实 ...
- centos 7 c++连接mysql的常用函数说明及使用样例
以下函数使用之前需安装mysql,并包含mysql.h头文件,设置好mysqlclient动态库 一.mysql_init() MYSQL * mysql_init(MYSQL *mysql); // ...
- Python 抓取网页tag操作
1. 获取操作tag 获取操作tag的接种方式: soup.find_all(name=None, attrs={}, recursive=True, text=None, limit=None, * ...
- Request 对象的主要方法
setAttribute(String name,Object):设置名字为 name 的 request 的参数值 getAttribute(String name):返回由 name 指定的属性值 ...
- Accord.NET重启4.0 开发
Accord.NET Framework是在AForge.NET基础上封装和进一步开发来的.功能也很强大,因为AForge.NET更注重与一些底层和广度,而Accord.NET Framework更注 ...