項目源代碼可訪問我的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哦!