项目源代码可访问我的 github:https://github.com/Spacider/Gather-and-store
如果觉得好的话请给个 star 哦~
项目初始
开发 IDE: IDEA 2018.03 JDK 1.8
开发环境: macOS 10.13.6 (如 windows 请对项目中部分路径进行改写)
数据库: Oracle 11g
正式开发
- 用 IDEA 创建一个 maven 项目,这个百度都可以找到,我在这里就不细讲了。
- 解决项目所有的依赖 jar 包,在项目下的 pom.xml 文件中添加如下内容:
<dependencies>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.39</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.6</version>
</dependency>
</dependencies>
第一阶段数据的采集
这里是我们向树莓派获取温度和湿度的模块示意图,通过 XML 文件的交互就可以获得我们需要的数据,获得到数据以后我们拆分得到的 XML 并向 log 文件中存入我们解析好的数据!
首先,模块结构:
然后开始编写代码:
- 我们创建一个包为 Server,然后创建一个类 DataServer,这个类的作用是用来模拟树莓派系统的,他的主要职能是接收 XML 文件,并返回对应的数据,这里是模拟,所以我们每次都返回一个相同的数据用于测试。
我们先定义个 Thread 类,以便于配合 while 语句来让 Server 端一直保持接收数据的状态。通过构造器将 Socket 对象传入 Thread 类中,通过 Socket 对象我们就可以对获取对应的流对接收与发送进行控制!
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket = socket;
}
@Override
public void run() {
}
}
接下来我们的认识就是重写 run 方法,我们需要接收客户端发来的 XML 文件,由于发送过程中每一行都有一个换行符,所以嵌套一个 BufferedReader
对象时,我们就可以通过 readLine()
方法来根据行读取文件,末尾结束的标志是 </Message>
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String str = null;
StringBuilder sb = new StringBuilder();
while ((str = br.readLine()) != null) {
sb.append(str + "\r\n");
// 如果读取到最后一行,则退出
if (str.trim().equals("</Message>")) {
break;
}
}
为了关闭资源方便,我们用到了关闭资源辅助类:
创建 util
包,创建 IOUtil
方法。
public class IOUtil {
public static void close(Closeable... closeableList) {
try {
for (Closeable closeable : closeableList) {
if (closeable != null) {
closeable.close();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
读取到 XML 文件时,我们就需要对相应的 Client 端返回一个新的 XML 文件,为了方便我们编写一个 SAXReaderHelper
类来协助我们的操作,编写一个方法 BackXml
来进行相关的处理。该方法通过解析从客户端发来的 xml 文件,获取 SensorAddress
值来模拟传感器的选择,从而返回不同的模拟数据。
- sensorAddress 为 16 采集到的数据为 10 位,前 8 位为湿度和温度数据,后两位为序号。
- sensorAddress 为 256 采集到的数据为 6 位,前四位为光照数据,后两位为序号。
- sensorAddress 为 1280 采集到的数据为 6 位,前四位为二氧化碳数据,后两位为序号。
/**
* 根据不同的 SensorAddress 返回不同的XML
* @param bris
*/
public static String BackXml(String sb){
ByteArrayInputStream bais = null;
// 把客户端传过来的 xml 转化为字符串存储
String TotalStr = sb.toString();
byte[] TotalBytes = TotalStr.getBytes();
bais = new ByteArrayInputStream(TotalBytes);
SAXReader reader = new SAXReader();
Document document = null;
String BackStr = null;
// 获取根节点
try {
document = reader.read(bais,"utf-8");
Element Message = document.getRootElement();
String str = null;
// 获取结点 SensorAddress 的值
String SensorAddress = Message.element("SensorAddress").getText();
if (SensorAddress.equals("16")){
str = "5d606f7802";
BackStr = getBakXml(str);
}else if (SensorAddress.equals("256")){
str = "5d6002";
BackStr = getBakXml(str);
}else if (SensorAddress.equals("1280")){
str = "5d6002";
BackStr = getBakXml(str);
}
} catch (DocumentException e) {
System.out.println("返回XML文件失败");
} finally {
IOUtil.close(bais);
}
return BackStr;
}
/**
* 拼接返回的 XML (提供模拟的 Server 端使用)
* @param DataStr
* @return
*/
public static String getBakXml(String DataStr){
String str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"+
"<Message>\r\n"+
"<SrcID>100</SrcID>\r\n"+
"<DstID>101</DstID>\r\n"+
"<DevID>2</DevID>\r\n"+
"<SensorAddress>0</SensorAddress>\r\n"+
"<Counter>0</Counter>\r\n"+
"<Cmd>3</Cmd>\r\n"+
"<Data>"+DataStr+"</Data>\r\n"+
"<Status>1</Status>\r\n"+
"</Message>\r\n";
return str;
}
在封装了方法以后,我们就可以一行解决问题:
// 根据不同的 SensorAddress 返回不同的XML
String BackStr = SAXReaderHelper.BackXml(sb.toString());
最后通过输出流再次将封装好的模拟数据发送给 Client
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write(BackStr.toCharArray());
pw.flush();
最后关闭资源~这样我们就编写好了一个模拟的树莓派 Server 端。通过 while(true)
写死循环,这样就可以一直不停的接收数据!
ServerSocket server = new ServerSocket(8888);
while (true){
Socket socket = server.accept();
new ServerThread(socket).start();
}
- Client 端编写
Client 端是发送 XML 文件到 Server 端的,其中还是用流进行传输。
首先我们编写 ClientReceiveHelper
类开进行辅助我们操作。
这是拼接发送的 XML 文件的方法!
/**
* 拼装发送的 XML 文件
* @param SensorAddress
* @param counter
* @return
*/
private final static String XmlToSend(String SensorAddress,String counter){
String str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n"+
"<Message>\r\n"+
"<SrcID>100</SrcID>\r\n"+
"<DstID>101</DstID>\r\n"+
"<DevID>2</DevID>\r\n"+
"<SensorAddress>"+SensorAddress+"</SensorAddress>\r\n"+
"<Counter>"+counter+"</Counter>\r\n"+
"<Cmd>3</Cmd>\r\n"+
"<Status>1</Status>\r\n"+
"</Message>\r\n";
return str;
}
客户端通过的是 TCP/IP 向 Server 端发送消息。
然后编写发送消息的方法 ClientGetXml
:
public final static void ClientGetXml(String SensorAddress,int counter){
}
通过 Socket 来开启一个连接
socket = new Socket("127.0.0.1", 8888);
加锁:考虑到一种特殊情况,socket 交替执行时可能会出现时间片用完的情况,这种情况下可能会导致上一个 socket 的数据传输给了下个 socket 使用,导致数据的错误!
通过发送 XML 让 Server 返回一个 XML 文件给我们,本阶段我们以获取到了上文 Server 发送的 XML 文件为正确。
synchronized (socket) {
os = socket.getOutputStream();
pw = new PrintWriter(os);
String str = XmlToSend(SensorAddress, counter + "");
pw.write(str.toCharArray());
pw.flush();
is = socket.getInputStream();
br = new BufferedReader(new InputStreamReader(is));
String Backstr = null;
// 拼接读取返回的 XML 文件
StringBuilder sb = new StringBuilder();
while ((Backstr = br.readLine()) != null) {
// 在末尾加入 \r\n 方便观察,也方便读取
sb.append(Backstr + "\r\n");
if (Backstr.trim().equals("</Message>")) {
break;
}
}
}
编写到这里, 你可以打印一下 BackStr,看下是否发生了死锁(可能未关闭资源或连接未断开等)和传输到的数据是否正确。
经过如上的编写,你就可以编写 Client 如:
方法中的参数为 SensorAddress
的值(判断温度湿度或光照或二氧化碳),和 counter
操作的传感器个数!ClientGetXml
就是上文我们编写的 Client 的操作方法。
public final class GuangClient {
public static void guangGetObj(){
ClientReceiveHelper.ClientGetXml("256",1);
}
}
如果你已经获得了 Server 端发送的数据,那么恭喜你,你这一阶段的编写已经取得了成功,可以进行下一阶段的编写!
本文中为代码详解,可能与源代码不一定相同,可以查看我的 github:https://github.com/Spacider/Gather-and-store 查看源代码与你的代码进行比对!如果觉得我写的好的话请给一个 star 哦!
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于