Java 使用Socket 實現基於DTU的TCP服務器 + 數據解析 + 心跳檢測

在物聯網時代,DTU的運用非常廣泛 ;環境監測中通過DTU將傳感器的數據遠程傳輸至雲服務器也是比較常見的用法。下面我來分享一下我的項目經驗

1.物理連接拓撲

2.服務器後臺流程

3.代碼

設備TCP服務器監聽線程

class SubRoutineThread implements Runnable{ 
	
	private int port; //接收數據端口
	private String proName; // 接收數據名稱
	private ParseData parseData; //解析數據對象
	
	public SubRoutineThread(int port,String proName,ParseData parseData){
		this.port=port;
		this.proName=proName;
		this.parseData=parseData;
	}
    public void run(){ 
    	SocketServer socketServer =new SocketServer();
    	try {
			socketServer.start(port,proName,parseData);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }

	public void setPort(int port) {
		this.port = port;
	}
	public void setProName(String proName) {
		this.proName = proName;
	} 
	public void setParseData(ParseData parseData) {
		this.parseData = parseData;
	}
}

 開啓監聽與數據接收

/**
 * TCP Server
 * @author yz
 *
 */
public class SocketServer { 
	private static Logger logger = Logger.getLogger(SocketServer.class); 
	private int port;
    private ParseData parseData;
    public String nameOfPro="";
    public boolean outputConsole=false;
    
    public void start(int port,String namePro, ParseData parseData) throws IOException {
    	this.port=port;       
        this.parseData=parseData;
        this.nameOfPro=namePro;
        
        ServerSocket serverSocket = new ServerSocket(port);  
        CommTool.printInfo(nameOfPro + " 正在監聽,端口:"+String.valueOf(port));
        while (true) {         	
            Socket socket = serverSocket.accept(); 
            new SocketThread(socket).start(); 
        } 
    } 
    //數據處理線程
    class SocketThread extends Thread { 
        private Socket socket; 
        private byte[] bytes; 
        public Date lastHeartPkDate = new Date();
        
     
        public Socket getSocket() { 
            return socket; 
        } 
 
        public void setSocket(Socket socket) { 
            this.socket = socket; 
        } 
 
        public SocketThread(Socket socket) { 
            this.socket = socket; 
        } 
 
        
        public void run() { 
        	CommTool.printInfo(nameOfPro + " 接受連接:" + socket.getInetAddress() + ":" + socket.getPort());
            try { 
            	InputStream ins= socket.getInputStream();  
            	
            	while(true) {
            		if(DateUtils.pastSeconds(lastHeartPkDate) > 60 ) 
            		{
            			logger.info(nameOfPro+",長時間未收到心跳數據,停止接收數據並斷開連接");
                    	CommTool.printInfo(nameOfPro+",長時間未收到心跳數據,停止接收數據並斷開連接");
        				break;
        			}	
            		
            		int readBytes = 0; 
            		int length = 0;
            		//循環接收,保證接收完整數據包
            		ByteBuffer buffer = ByteBuffer.allocate(4096);
            		while(ins.available() > 0) {
            			byte[] temp = new byte[ins.available()];
            			length = ins.read(temp);
            			buffer.put(temp,readBytes,length);
                    	readBytes += length;
            		}
            		
            		bytes = buffer.array();
            		if(readBytes > 0) {
            			CommTool.printInfo(nameOfPro+",接收到數據," + readBytes + "bytes");	
                		
                        //解析 ,需要判定是否是心跳包               
                        if(!parseData.parse(bytes)){
                        	logger.info(nameOfPro+",接收數據解析失敗");
                        	CommTool.printInfo(nameOfPro+",數據解析失敗");
                        	return;
                        }
                        if(!parseData.isHeartPack()) {
                        	//存儲	
                            if(!parseData.save()){
                            	logger.info(nameOfPro+",數據存儲失敗");
                            	CommTool.printInfo(nameOfPro+",數據存儲失敗");
                            	return;
                            } 
                            
                        	CommTool.printInfo(nameOfPro+",數據解析存儲成功");
                        }
                        else {
                        	lastHeartPkDate = parseData.getLastHeartPackDate();
                        }
                        
            		}
            	}
            	
            	
            } catch (Exception e) { 
            	logger.error(nameOfPro+",未知錯誤,"+e.toString()); 
            	CommTool.printInfo(nameOfPro+",未知錯誤,"+e.toString());            	
            } finally { 
                if (socket != null) { 
                    if (!socket.isClosed()) { 
                        try { 
                            socket.close(); 
                        } catch (IOException e) { 
                            e.printStackTrace(); 
                        } 
                    } 
                } 
            } 
 
        } 
    
    } 

} 

數據包解析接口類

/**
 * 解析接口 
 * @author 70910
 *
 */
public interface ParseData {
   boolean parse(byte[] bytes);
   boolean save();  
   void setDrcProInfo(DrcProInfo drcProInfo);  
   //是否是心跳包
   boolean isHeartPack();
   //獲取最後心跳時間
   Date getLastHeartPackDate();
}

設備數據包解析實體類

/**
 * 解析負氧離子數據
 * @author yangze
 * 2019-08-21
 *
 */
public class AnionSensorDataParse implements ParseData {
	private static Logger logger = Logger.getLogger(AnionSensorDataParse.class); 
	public DrcProInfo drcProInfo;	
	private int deviAddr;	//設備地址
	private int funcCode;	//功能碼
	private int dataLength; //數據長度
	private int anionData; 
	private String typeOfPk;
	private byte[] srcData;
	
	private DeviceInfo deviceInfo = new DeviceInfo();
	private Map<String, ParamInfo> name2ParamInfoMap= new HashMap<String, ParamInfo>();
	
	private static Connection conn = null;
	
	private boolean isHeartPk = false;
	private Date lastHeartPkDate = new Date();
	
    public AnionSensorDataParse(){}
    	
    
 	
    @Override
    public boolean parse(byte[] data){
    	srcData = Arrays.copyOfRange(data, 0, 9);
    	String preStr=drcProInfo.getParseDataName();
		int indexNow=0;
		
		if(是心跳包){
			lastDataTime = new Date();
			isHeartPk = true;
			return true;
			
		}
		else if(是數據包){
			isHeartPk = false;
			//解析數據
			return true;
		}
		else{
			isHeartPk = false;
			CommTool.printInfo(preStr+",數據包類型不正確");
			logger.error(preStr+",數據包類型不正確");
		   return false;
		}
		
	}
    @Override
 	public boolean isHeartPack() {
 		
 		return this.isHeartPk;
 	}
 	
 	@Override
    public Date getLastHeartPackDate() {
    	return this.lastDataTime;
    }
 	
   	
	@Override	
    public boolean save() {	
		//連接數據庫
		//存儲數據
		return true;
	}

	@Override
	public void setDrcProInfo(DrcProInfo drcProInfo) {
		this.drcProInfo=drcProInfo;
	}

	
}

結束語,以上只是我項目中的一些想法,提供的代碼示例並不完整,如果需要可以和我一起探討。歡迎給出意見。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章