TCP Server端实现,一定要用NIO来保证不被堵塞,否则将会造成服务器CPU飙升(切记,切记)
创建缓冲区(static map) 一定要用线程安全的map来实现,否则多个请求将造成map中存在您要的东西,而get为空的情况(切记),我这里用的:ConcurrentHashMap<String,SocketChannel> cacheThread =new ConcurrentHashMap<>();
主要包括以下几个部分
1.TCP server端开发
2.sh服务端启动脚本
3.服务端报文分析
4.静态json接口生成
5.TCP线程池清理
6.报文回复
1. server端源码(NIO实现,在用-超级稳定)
import com.google.gson.Gson;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class IOTServer {
private int tcpPort = 9092;
private String path = "";
private static boolean cmdLock = true;
private static long updateLog = 0;
//更换为线程安全的map
private static ConcurrentHashMap<String,SocketChannel> cacheThread =new ConcurrentHashMap<>();
private Timer timer ;
private int size = 256;//256k
public void log(String msg){
if(msg!=null&&!msg.equals("")){
if(updateLog==0||updateLog<=System.currentTimeMillis()){
System.out.println("clear.........");
//清理
String datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
msg = datetime+" : "+msg;
if(!"".equals(path)){
writeFile(path+ File.separator+"log.html",msg+"\r\n",false);
}
System.out.println(msg);
updateLog= System.currentTimeMillis()+10*60*1000;//10分钟1次
}else{
//white log
String datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
msg = datetime+" : "+msg;
if(!"".equals(path)){
writeFile(path+File.separator+"log.html",msg+"\r\n",true);
}
System.out.println(msg);
}
}
}
//append file
private void writeFile(String fileFullPath,String content,boolean b) {
FileOutputStream fos = null;
try {
File f = new File(fileFullPath);
if(!f.exists()) f.createNewFile();//创建新文件
//wt
fos = new FileOutputStream(fileFullPath, b);
fos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}finally{
if(fos != null){
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public IOTServer(String path,String port,String size){
this.tcpPort = Integer.parseInt(port);
this.path = path;
this.size = Integer.parseInt(size);
}
public void start() throws IOException {
// Selector: multiplexor of SelectableChannel objects
Selector selector = Selector.open(); // selector is open here
// ServerSocketChannel: selectable channel for stream-oriented listening sockets
ServerSocketChannel crunchifySocket = ServerSocketChannel.open();
InetSocketAddress crunchifyAddr = new InetSocketAddress(tcpPort);
// Binds the channel's socket to a local address and configures the socket to listen for connections
crunchifySocket.bind(crunchifyAddr);
// Adjusts this channel's blocking mode.
crunchifySocket.configureBlocking(false);
int ops = crunchifySocket.validOps();
SelectionKey selectKy = crunchifySocket.register(selector, ops, null);
log("Waiting client connect ... "+tcpPort);
// Infinite loop..
// Keep server running
while (true) {
// Selects a set of keys whose corresponding channels are ready for I/O operations
selector.select();
// token representing the registration of a SelectableChannel with a Selector
Set<SelectionKey> crunchifyKeys = selector.selectedKeys();
Iterator<SelectionKey> crunchifyIterator = crunchifyKeys.iterator();
while (crunchifyIterator.hasNext()) {
SelectionKey myKey = crunchifyIterator.next();
// Tests whether this key's channel is ready to accept a new socket connection
if (myKey.isAcceptable()) {
SocketChannel crunchifyClient = crunchifySocket.accept();
// Adjusts this channel's blocking mode to false
crunchifyClient.configureBlocking(false);
// Operation-set bit for read operations
crunchifyClient.register(selector, SelectionKey.OP_READ);
log("Connection Accepted: " + crunchifyClient.getLocalAddress() + "\n");
// Tests whether this key's channel is ready for reading
} else if (myKey.isReadable()) {
SocketChannel crunchifyClient = (SocketChannel) myKey.channel();
try {
ByteBuffer crunchifyBuffer = ByteBuffer.allocate(size);
int readCount = crunchifyClient.read(crunchifyBuffer);
//获取did
String did = report(crunchifyBuffer.array(),readCount);
if (!did.equals("")) {
//晶合 002A00203235323916473831
if (did.length() == 24) {
//根据did去队列中获取命令,并更新命令状态
overJH(did, crunchifyClient);
log("close JH SocketChannel.");
crunchifyClient.close();
}
//精讯
if (did.length() == 12) {
//只有符合did的才开启线程,并传入SocketChannel/并把该did和Thread绑定记录到cache
cacheThread.put(did, crunchifyClient);
log("Waiting JX data. did->"+did+"\t isconnected->"+crunchifyClient.isConnected()+"\t"+"cacheThread size - >"+cacheThread.size()+"\t cacheThread keys:"+toKeys(cacheThread.keySet()) );
over(crunchifyClient,"01");
}
} else {
if("error".equals(did)){
crunchifyClient.close();
} else{
log("close Error SocketChannel.");
over(crunchifyClient,"00");
crunchifyClient.close();
}
}
}catch (Exception e){
//log(e.getLocalizedMessage());
}
}
crunchifyIterator.remove();
}
}
}
private String toKeys(Set<String> set){
StringBuilder sb = new StringBuilder("");
for(String s:set){
sb.append(s+",");
}
return sb.toString();
}
private int cycleIndex = 0;
public void cycle(){
timer = new Timer();
//3秒后获取执行命令
timer.schedule(new TimerTask() {
@Override
public void run() {
if(cmdLock){
cmdLock = false;
sendCmd();
if(cycleIndex>200){ //10分钟1次
log("----------- clear cmds ---------");
JDBC.checkCmd();
cycleIndex=0;
}
cycleIndex++;
cmdLock= true;
}
}
},0,2950);
}
//发送命令
public void sendCmd(){
List<Map> cmdLines = JDBC.getCmd();
for(Map line:cmdLines){ //循环执行可操作命令
String cmd = (String) line.get("cmd");
String did = (String) line.get("did");
log("Get execute did->"+did+" cmd->"+cmd);
if(cmd!=null && did!=null && did.length()==12){//精讯
SocketChannel socketChannel = cacheThread.get(did);
log("socketChannel - >"+socketChannel);
if(socketChannel!=null){
log("socketChannel connected ->"+socketChannel.isConnected()+"\t isOpen->"+socketChannel.isOpen());
try {
over(socketChannel, cmd);
JDBC.removeCmd(line.get("id").toString());
log("Execute cmd ok!");
}catch (Exception e){
log(e.getLocalizedMessage());
}
}
}
}
}
//callback
public void overJH(String did,SocketChannel socketChannel){
String cmdSQL = "select id,cmd from iot_cmds where did='"+did+"' and status=0 order by receive_time desc limit 1";
List<Map> list = JDBC.query(cmdSQL);
if(list!=null&&list.size()>0){
String cmd = (String)list.get(0).get("cmd");
Integer id = (Integer)list.get(0).get("id");
if(id!=null&&cmd!=null){//最新的命令未执行
log("===JH==="+cmd+" id->"+id+" execute start.");
try {
over(socketChannel, cmd);
}catch (Exception e){
e.printStackTrace();
}
//更新命令状态
JDBC.removeCmd(id+"");
log("===JH==="+cmd+" execute ok.");
}else{
log("===JH==="+cmd+" id->"+id+" no execute.");
}
}else{
log("===JH=== no cmd.");
}
}
//over
public void over(SocketChannel socketChannel,String newData) throws IOException {
if(newData.startsWith("{")){
ByteBuffer bb = ByteBuffer.allocate(newData.getBytes().length);
bb.clear();
bb.put(newData.getBytes());
bb.flip();
while (bb.hasRemaining()) {
socketChannel.write(bb);
}
}else {
ByteBuffer bb = ByteBuffer.allocate(toBytes(newData).length);
bb.clear();
bb.put(toBytes(newData));
bb.flip();
while (bb.hasRemaining()) {
socketChannel.write(bb);
}
}
}
//写入json
private void outJson(String datetime,String did,String awd,String asd,String sun,String co2,String ph,String lwd,String lsd,String ddv,
String phws,String phwd,String phqy,String phyl,
String UVA,String GH,String LD,String sTMP2,String sHR2,String sEC2,String sTMP3,String sHR3,String sEC3,
String sTMP4,String sHR4,String ph2,
String j01,String j02,String j03,String j04,String j05,String j06,String j07,String j08,String j09,String j0a,String bg,
int readCount,
String jb_time,String tf1_time,String tf2_time,String tf3_time,String js_time,String up_time){
//json
String json = "{\"datetime\":\""+datetime+"\"," +
"\"did\":\""+did+"\"," +
"\"awd\":\""+awd+"\"," +
"\"asd\":\""+asd+"\"," +
"\"sun\":\""+sun+"\"," +
"\"co2\":\""+co2+"\"," +
"\"ph\":\""+ph+"\"," +
"\"lwd\":\""+lwd+"\"," +
"\"lsd\":\""+lsd+"\"," +
"\"ddv\":\""+ddv+"\"," +
"\"phws\":\""+phws+"\"," +
"\"phwd\":\""+phwd+"\"," +
"\"phqy\":\""+phqy+"\"," +
"\"phyl\":\""+phyl+"\"," +
"\"uva\":\""+UVA+"\","+
"\"gh\":\""+GH+"\","+
"\"ld\":\""+LD+"\","+
"\"stmp2\":\""+sTMP2+"\","+
"\"shr2\":\""+sHR2+"\","+
"\"sec2\":\""+sEC2+"\","+
"\"stmp3\":\""+sTMP3+"\","+
"\"shr3\":\""+sHR3+"\","+
"\"sec3\":\""+sEC3+"\","+
"\"stmp4\":\""+sTMP4+"\","+
"\"shr4\":\""+sHR4+"\","+
"\"ph2\":\""+ph2+"\"," +
"\"j01\":\""+j01+"\"," +
"\"j02\":\""+j02+"\"," +
"\"j03\":\""+j03+"\"," +
"\"j04\":\""+j04+"\"," +
"\"j05\":\""+j05+"\"," +
"\"j06\":\""+j06+"\"," +
"\"j07\":\""+j07+"\"," +
"\"j08\":\""+j08+"\"," +
"\"j09\":\""+j09+"\"," +
"\"j0a\":\""+j0a+"\"," +
"\"bg\":\""+bg+"\"," +
"\"bzise\":\""+readCount+"\"," +
"\"jb_time\":\""+jb_time+"\"," +
"\"tf1_time\":\""+tf1_time+"\"," +
"\"tf2_time\":\""+tf2_time+"\"," +
"\"tf3_time\":\""+tf3_time+"\"," +
"\"js_time\":\""+js_time+"\"," +
"\"up_time\":\""+up_time+"\"}";
//输入到日志
log(json);
//写到json目录/did.json文件中
writeJson(json,did);
}
private String FEDC01(String hex,String datetime,int readCount){
try {
String did = hex.substring(6,18); //did
//上报数值
String awd = hex.substring(32,40);//空气温度
String asd = hex.substring(40,48);//空气湿度
String lwd = hex.substring(48,56);//土壤温度
String lsd = hex.substring(56,64);//土壤湿度
String co2 = hex.substring(64,72);//二氧化碳
String sun = hex.substring(72,80);//光照强度
String ph = hex.substring(80,88);//土壤ph
String ddv = hex.substring(88,96);//电导率
//上报继电器
String j01 = hex.substring(98,100); //继电器01的状态
String j02 = hex.substring(102,104); //继电器02的状态
String j03 = hex.substring(106,108); //继电器03的状态
String j04 = hex.substring(110,112); //继电器04的状态
String j05 = hex.substring(114,116); //继电器05的状态
String j06 = hex.substring(118,120); //继电器06的状态
String j07 = hex.substring(122,124); //继电器07的状态
String j08 = hex.substring(126,128); //继电器08的状态
String j09 = hex.substring(130,132); //继电器09的状态
String j0a = hex.substring(134,136); //继电器10的状态
//卷被,通风1,通风2,通风3,背景,上报间隔
String jb_time = hex.substring(136,140); //卷被时间间隔
String tf1_time = hex.substring(140,144); //通风1时间间隔
String tf2_time = hex.substring(144,148); //通风2时间间隔
String tf3_time = hex.substring(148,152); //通风3的时间间隔
String js_time = hex.substring(152,156); //浇水时间
String bg = hex.substring(156,160);//背景图
String up_time = hex.substring(160,164);//上报间隔
//新增8,8,8,8
String phws = "";//风速
if(hex.length()>172) phws = hex.substring(164,172);
String phwd = "";//风向
if(hex.length()>180) phwd = hex.substring(172,180);
String phqy = "";//气压
if(hex.length()>188) phqy=hex.substring(180,188);
String phyl = "";//雨量
if(hex.length()>196) phyl=hex.substring(188,196);
if(did!=null&&!did.equals("")){
awd = trans(awd,true);
asd = trans(asd,true);
sun = trans(sun,false);
co2 = trans(co2,false);
ph = trans2(ph);
lwd = trans(lwd,true);
lsd = trans(lsd,true);
ddv = trans(ddv,false);
jb_time = trans10(jb_time);
tf1_time = trans10(tf1_time);
tf2_time = trans10(tf2_time);
tf3_time = trans10(tf3_time);
js_time = trans10(js_time);
up_time = trans10(up_time);
phws = trans(phws,true);
phwd = trans(phwd,true);
phqy = trans2(phqy);
phyl = trans(phyl,false);
//输出json
outJson(datetime,did,awd,asd,sun,co2,ph,lwd,lsd,ddv,
phws,phwd,phqy,phyl,
"","","","","","","","","",
"","","",
j01,j02,j03,j04,j05,j06,j07,j08,j09,j0a,bg,
readCount,
jb_time,tf1_time,tf2_time,tf3_time,js_time,up_time);
}
return did;
}catch (Exception e){}
return "";
}
private String FEDC03(String hex,String datetime,int readCount){
try {
// FEDC03 6
// 3E0BF337EF19 12
// 0000004A03003C 14
// 000000B1 40
// 0000013B 48
// 00000005 56
// 00000000 64
// 000000CE 72
// 00000000 80
// 000000D7 88
// 00000000 96
// 000000CF 104
// 00000000 112
// 000000D0 120
// 00000000 128
// 000002BC 136
// 00000221 144
// 00000014
String did = hex.substring(6,18); //did
//上报数值
String awd = hex.substring(32,40);//空气温度
String asd = hex.substring(40,48);//空气湿度
String sun = hex.substring(48,56);//光照
String co2 = hex.substring(56,64);//co2
String lwd = hex.substring(64,72);//土壤温度
String lsd = hex.substring(72,80);//土壤湿度
String lwd1 = hex.substring(80,88);//土壤温度 1
String lsd1 = hex.substring(88,96);//土壤湿度 1
String lwd2 = hex.substring(96,104); //土壤温度 2
String lsd2 = hex.substring(104,112); //土壤湿度 2
String lwd3 = hex.substring(112,120); //土壤温度 3
String lsd3 = hex.substring(120,128); //土壤湿度 3
String ph = hex.substring(128,136); //土壤温度 4
String ph1 = hex.substring(136,144); //土壤湿度 4
if(did!=null&&!did.equals("")){
awd = trans(awd,true);
asd = trans(asd,true);
sun = trans(sun,false);
co2 = trans(co2,false);
lwd = trans(lwd,true);
lsd = trans(lsd,true);
lwd1 = trans(lwd1,true);
lsd1 = trans(lsd1,true);
lwd2 = trans(lwd2,true);
lsd2 = trans(lsd2,true);
lwd3 = trans(lwd3,true);
lsd3 = trans(lsd3,true);
ph = trans2(ph);
ph1 = trans2(ph1);
//输出json
outJson(datetime,did,awd,asd,sun,co2,ph,lwd,lsd,"",
"","","","",
"","","",lwd1,lsd1,"",lwd2,lsd2,"",
lwd3,lsd3,ph1,
"","","","","","","","","","","",
readCount,
"","","","","","");
}
return did;
}catch (Exception e){}
return "";
}
private String JH(byte[] bytes,int readCount){
String result = new String(bytes).trim();
log("JH->"+result);
if(result!=null&&!result.equals("")){
Map info = new Gson().fromJson(result,Map.class);
String did = (String)info.get("U"); //设备id
String t2 = info.get("t2").toString();//数据上报间隔
List awds = (List)info.get("TMP"); //空气温度
List asds = (List)info.get("HR"); //空气湿度
List lwds = (List)info.get("sTMP"); //土壤温度
List lsds = (List)info.get("sHR"); //土壤湿度
List suns = (List)info.get("ILL"); //光照强度
List co2s = (List)info.get("CO2"); //二氧化碳
List phs = (List)info.get("sPH"); //ph值
List ddvs = (List)info.get("sEC"); //电导率
List phwss = (List)info.get("FS"); //风速
List phwds = (List)info.get("FX"); //风向
List phyls = (List)info.get("RV"); //雨量
List phqys = (List)info.get("PRS"); //气压
List UVAs = (List) info.get("UVA");//紫外线 w/㎡
List GHs = (List) info.get("GH");//光合 w/㎡
List LDs = (List) info.get("LD");//露点 °C
List sTMP2s = (List) info.get("sTMP2");//2号传感器 °C
List sHR2s = (List) info.get("sHR2");//2号传感器 %RH
List sEC2s = (List) info.get("sEC2");//2号传感器 us/cm
List sTMP3s = (List) info.get("sTMP3");//3号传感器
List sHR3s = (List) info.get("sHR3");//3号传感器
List sEC3s = (List) info.get("sEC3");//3号传感器
//list
String awd = tran100(awds,2);
String asd = tran100(asds,2);
String lwd = tran100(lwds,2);
String lsd = tran100(lsds,2);
String sun = tran100(suns,2);
String co2 = tran100(co2s,0);
String ph = tran100(phs,2);
String ddv = tran100(ddvs,0);
String phws = tran100(phwss,2);
String phwd = fx100(phwds);
String phyl = tran100(phyls,1);
String phqy = tran100(phqys,2);
String UVA = tran100(UVAs,1);
String GH = tran100(GHs,0);
String LD = tran100(LDs,1);
String sTMP2 = tran100(sTMP2s,2);
String sHR2 = tran100(sHR2s,2);
String sEC2 = tran100(sEC2s,0);
String sTMP3 = tran100(sTMP3s,2);
String sHR3 = tran100(sHR3s,2);
String sEC3 = tran100(sEC3s,0);
String datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
//json
outJson(datetime,did,awd,asd,sun,co2,ph,lwd,lsd,ddv,
phws,phwd,phqy,phyl,
UVA,GH,LD,sTMP2,sHR2,sEC2,sTMP3,sHR3,sEC3,
"","","",
"","","","","","","","","","","",
readCount,
"","","","","","");
return did;
}
return "";
}
public String report(byte[] bytes,int readCount){
String hex = byteToHex(bytes);
if(hex.startsWith("000000000")){ return "error"; }
log("source->"+hex);
if(hex!=null&&(hex.startsWith("FEDC01") || hex.startsWith("FEDC03") )){ //精讯
String datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
if(hex.startsWith("FEDC01")){
log("JH->FEDC01-----------1");
return FEDC01(hex,datetime,readCount);
}
if(hex.startsWith("FEDC03")){
log("JH->FEDC03-----------2");
return FEDC03(hex,datetime,readCount);
}
}else if(hex!=null&&(hex.startsWith("7B"))){ //晶合
return JH(bytes,readCount);
}else if(hex!=null&&(hex.length()>16)&&hex.substring(12,16).equals("AAAA")){
return AA(hex,readCount);
}else{
log("Error head -> "+hex);
}
return "";
}
//我们自己的设备 4合一
//send 26 20 44 68 22 18 AA AA 01 03 12 01 BE 00 76 00 00 00 00 00 00 03 11 00 00 00 00 00 32 84 1D
//receive 260200440680220180AA0AA0010030120010B80000750000000000000000000020DC0000000000000000320F90BD
// 262044682218AAAA
// 01030C014300B800000000000000006631
// 0103020015798B01
// 0302FFFFB9F4
// 01030400018894CC5C
// 0103020000B844
public String AA(String hex,int readCount){
String did = hex.substring(0,16); //did
log("AA->"+did);
if("262044682218AAAA".equals(did)){
String airsd = null;
if(hex.length()>26) airsd = hex.substring(22,26);
String airwd = null;
if(hex.length()>30) airwd = hex.substring(26,30);
String phws = null;
if(hex.length()>60) phws = hex.substring(56,60);
String phwd = null;
if(hex.length()>74) phwd = hex.substring(70,74);
String phqy = null;
if(hex.length()>92) phqy = hex.substring(84,92);
String phyl = null;
if(hex.length()>106) phyl = hex.substring(102,106);
airsd = transAA(airsd,true);
airwd = transAA(airwd,true);
phws = transAA(phws,true);
phwd = transAA(phwd,false);
log(phwd);
//获得的数值为角度值,需要除以22.5 取整标识方向
if(phwd!=null&&!phwd.equals("")){
phwd = (int)(Double.parseDouble(phwd)/22.5)+"";
}
log(phwd);
phqy = trans2(phqy);
phyl = transAA(phyl,true);
String datetime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
outJson(datetime,did,airwd,airsd,"","","","","","",
phws,phwd,phqy,phyl,
"","","","","","","","","",
"","","",
"","","","","","","","","","","",
readCount,
"","","","","","");
}
return did;
}
private String transAA(String info,boolean xs){
if(info==null||info.length()!=4) return "";
String result = Long.parseLong(info,16)+"";
if(xs) result= Long.parseLong(info,16)*0.1+""; //空气温度
DecimalFormat format = new DecimalFormat("0.00");
return format.format(new BigDecimal(result));
}
//单位值转换
private String tran100(List list,int level){
String awd = "";
if(list!=null&&list.size()>0){
String tt =list.get(list.size()-1).toString();
if(level==0) return tt;
if(level==1){
String result = Double.parseDouble(tt)/10+"";
DecimalFormat format = new DecimalFormat("0.00");
return format.format(new BigDecimal(result));
}
if(level==2){
String result = Double.parseDouble(tt)/100+"";
DecimalFormat format = new DecimalFormat("0.00");
return format.format(new BigDecimal(result));
}
}
return awd;
}
private String fx100(List list){
String value = "";
if(list!=null&&list.size()>0){
String tt =list.get(list.size()-1).toString();
if(tt.startsWith("0")) return "15";
if(tt.startsWith("1")) return "1";
if(tt.startsWith("2")) return "3";
if(tt.startsWith("3")) return "5";
if(tt.startsWith("4")) return "7";
if(tt.startsWith("5")) return "9";
if(tt.startsWith("6")) return "11";
if(tt.startsWith("7")) return "13";
}
return value;
}
//16进制转具体值
private String trans(String info,boolean xs){
if(info==null||info.length()!=8) return "";
String result = Long.parseLong(info,16)+"";
if(xs) result= Long.parseLong(info,16)*0.1+""; //空气温度
DecimalFormat format = new DecimalFormat("0.00");
return format.format(new BigDecimal(result));
}
private String trans2(String info){
if(info==null||info.length()!=8) return "";
String result = Long.parseLong(info,16)*0.01+"";
DecimalFormat format = new DecimalFormat("0.00");
return format.format(new BigDecimal(result));
}
//转10进制
private String trans10(String info){
if(info==null||info.length()!=4) return "";
String result = Long.parseLong(info,16)+"";
return result;
}
/**
* byte数组转hex
* @param bytes
* @return
*/
private static final char[] HEX_CHAR = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
public String byteToHex(byte[] bytes){
char[] buf = new char[bytes.length * 2];
int index = 0;
for(byte b : bytes) { // 利用位运算进行转换,可以看作方法一的变种
buf[index++] = HEX_CHAR[b >>> 4 & 0xf];
buf[index++] = HEX_CHAR[b & 0xf];
}
return new String(buf);
}
/**
* string转byte
* @param str
* */
public static byte[] toBytes(String str) {
if(str == null || str.trim().equals("")) {
return new byte[0];
}
byte[] bytes = new byte[str.length() / 2];
for(int i = 0; i < str.length() / 2; i++) {
String subStr = str.substring(i * 2, i * 2 + 2);
bytes[i] = (byte) Integer.parseInt(subStr, 16);
}
return bytes;
}
//json make
public void writeJson(String json,String id){
writeFile(path+File.separator+"json"+File.separator+id+".json",json,false);
}
public static void main(String args[]) throws Exception{
Properties pro = new Properties();
pro.load(IOTServer.class.getClassLoader().getResourceAsStream("config.properties"));
String port = pro.getProperty("PORT");
String path = pro.getProperty("PATH");
String size = pro.getProperty("SIZE");
cacheThread.clear();//清掉缓存
IOTServer is = new IOTServer(path,port,size);
is.cycle(); //监听命令
is.start();//启动server
}
}
2. sh服务端脚本
#!/bin/sh
chmod 777 ./log.html
chmod 777 ./nohup.out
nohup java IOTServer &
3. 物联网协议解析
原报文
EF BF BD EF BF BD 01 3E 0B EF BF BD EF BF BD EF BF BD EF BF BD 00 00 00 02 03 00 30 00 00 01 39 00 00 01 EF BF BD 00 00 00 EF BF BD 00 00 00 00 00 00 01 13 00 00 00 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF BF BD 00 00 00 00 00 EF BF BD EF BF BD 01 3E 0B EF BF BD EF BF BD EF BF BD EF BF BD 00 00 00 04 03 00 30 00 00 01 39 00 00 01 EF BF BD 00 00 00 EF BF BD 00 00 00 00 00 00 01 13 00 00 00 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF BF BD 00 00 00 00 00 EF BF BD EF BF BD 01 3E 0B EF BF BD EF BF BD EF BF BD EF BF BD 00 00 00 06 03 00 30 00 00 01 39 00 00 01 EF BF BD 00 00 00 EF BF BD 00 00 00 00 00 00 01 13 00 00 00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF BF BD 00 00 00 00 00 EF BF BD EF BF BD 01 3E 0B EF BF BD EF BF BD EF BF BD EF BF BD 00 00 00 08 03 00 30 00 00 01 39 00 00 01 EF BF BD 00 00 00 EF BF BD 00 00 00 00 00 00 01 13 00 00 00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF BF BD 00 00 00 00 00 EF BF BD EF BF BD 01 3E 0B EF BF BD EF BF BD EF BF BD EF BF BD 00 00 00
转化后报文
EF EF 01 3E 0B EF EF EF EF 00 00 00 02
03 00 30
00 00 01 39
00 00 01 EF
00 00 00 EF
00 00 00 00
00 00 01 13
00 00 00 1B
00 00 00 00
00 00 00 00
00 00 00 00
00 00 00 00
00 00 01 EF
00 00 00 00
00
EF EF 01 3E 0B EF EF EF EF 00 00 00 04 03 00 30 00 00 01 39 00 00 01 EF 00 00 00 EF 00 00 00 00 00 00 01 13 00 00 00 1C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF 00 00 00 00 00
EF EF 01 3E 0B EF EF EF EF 00 00 00 06 03 00 30 00 00 01 39 00 00 01 EF 00 00 00 EF 00 00 00 00 00 00 01 13 00 00 00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF 00 00 00 00 00
EF EF 01 3E 0B EF EF EF EF 00 00 00 08 03 00 30 00 00 01 39 00 00 01 EF 00 00 00 EF 00 00 00 00 00 00 01 13 00 00 00 1D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF 00 00 00 00 00
EF EF 01 3E 0B EF EF EF EF 00 00 00
* 报文分析
* EFEF01 为协议头 6位
* EF EF 01 3E 0B EF EF EF EF 为设备id 12位
* 00 00 00 02 SESSIONID 8位
* 03 数据报头 2位
* 00 30 数据字节长度 48字节 也就是说后边截取48位为数据 4位
* -----32位-----
* 00 00 01 39 为温度,转10进制为313,每个单位代表0.1℃,则温度为 31.3℃ 1
* 00 00 01 EF 为空气湿度,转10进制为495,每个单位代表0.1%RH,则湿度为 49.5%RH 2
* 00 00 00 EF 为光照强度,转10进制为239,每个单位代表239Lux,则光强为 239Lux 3
* 00 00 00 00 为PH,转10进制为0,每个代为代表0.1,则PH为0, 4
* 00 00 01 13 为土壤温度,转10进制为275,每个单位代表0.1℃,则土壤温度为 27.5℃ 5
* 00 00 00 1B 为土壤湿度,转10进制为27,每个单位代表0.1%RH, 则土壤湿度为 2.7%RH 6
* 00 00 00 00 为电导率,转10进制为0,每个单位代表1ug/cm,则电导率为 0ug/cm 7
* 00 00 00 00 为有效辐射,转10进制为0,每个单位代表1W/㎡,则辐射为 0W/㎡ 8
* 00 00 00 00 为太阳总辐射,转10进制为0,每个单位代表1W/㎡,则总辐射为 0W/㎡ 9
* 00 00 00 00 为紫外线,转10进制为0,每个单位代表1W/㎡,则紫外线为 0W/㎡ 10
* 00 00 01 EF 为CO2浓度,转10进制为495,每个单位为1ppm,则CO2浓度为 495ppm 11
* 00 00 00 00 为雨量,转10进制为0,每个单位为1mm,则雨量为 0mm 12
server端报文日志
4.静态json接口生成
//json make
public void writeJson(String json,String id){
writeFile(path+File.separator+"json"+File.separator+id+".json",json,false);
}
把 json内容写到 应用服务器 json目录下/设备ID.json文件,外网可访问。(避免大量接口请求的压力)
json格式如下
{
"datetime": "2019-06-18 18:12:03", //采集时刻
"did": "3E0BEFEFEFEF", //设备id
"awd": "30.20",//空气温度
"asd": "49.5", //空气湿度
"sun": "544", //光照强度
"ph": "0.0", //ph值
"lwd": "27.20", //土壤温度
"lsd": "4.0",//土壤湿度
"ddv": "0",//电导率
"er": "0", //有效辐射
"ar": "0", //总辐射
"uv": "0", //紫外线
"co2": "495", //二氧化碳浓度
"rain": "0" //降雨量
}
感谢您的支持,写的文章如对您有所帮助,开源不易,请您打赏,谢谢啦~