2017-04-25 36 views
3

我正在研究一種可以通過傳感器測量一些讀數的設備。設備由Android應用運行。我必須從TCP層讀取數據。這是爲了在發送TCP數據如何在TCP端口上使用二進制通信協議發送數據包

TcpClient.java

import android.util.Log; 

import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.InputStreamReader; 
import java.io.OutputStreamWriter; 
import java.io.PrintWriter; 
import java.net.InetAddress; 
import java.net.Socket; 

/** 
* Created by shahbaz on 25/4/17. 
*/ 

public class TcpClient { 

public static final String SERVER_IP = "192.168.1.76"; //server IP address 
public static final int SERVER_PORT = 1800; 
// message to send to the server 
private String mServerMessage; 
// sends message received notifications 
private OnMessageReceived mMessageListener = null; 
// while this is true, the server will continue running 
private boolean mRun = false; 
// used to send messages 
private PrintWriter mBufferOut; 
// used to read messages from the server 
private BufferedReader mBufferIn; 

/** 
    * Constructor of the class. OnMessagedReceived listens for the messages received from server 
    */ 
public TcpClient(OnMessageReceived listener) { 
    mMessageListener = listener; 
} 

/** 
* Sends the message entered by client to the server 
* 
* @param message text entered by client 
*/ 
public void sendMessage(String message) { 
    if (mBufferOut != null && !mBufferOut.checkError()) { 
     mBufferOut.println(message); 
     mBufferOut.flush(); 
    } 
} 

/** 
* Close the connection and release the members 
*/ 
public void stopClient() { 

    mRun = false; 

    if (mBufferOut != null) { 
     mBufferOut.flush(); 
     mBufferOut.close(); 
    } 

    mMessageListener = null; 
    mBufferIn = null; 
    mBufferOut = null; 
    mServerMessage = null; 
} 

public void run() { 

    mRun = true; 

    try { 
     //here you must put your computer's IP address. 
     InetAddress serverAddr = InetAddress.getByName(SERVER_IP); 

     Log.e("TCP Client", "C: Connecting..."); 

     //create a socket to make the connection with the server 
     Socket socket = new Socket(serverAddr, SERVER_PORT); 

     try { 

      //sends the message to the server 
      mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); 

      //receives the message which the server sends back 
      mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream())); 


      //in this while the client listens for the messages sent by the server 
      while (mRun) { 

       mServerMessage = mBufferIn.readLine(); 

       if (mServerMessage != null && mMessageListener != null) { 
        //call the method messageReceived from MyActivity class 
        mMessageListener.messageReceived(mServerMessage); 
       } 

      } 

      Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'"); 

     } catch (Exception e) { 

      Log.e("TCP", "S: Error", e); 

     } finally { 
      //the socket must be closed. It is not possible to reconnect to this socket 
      // after it is closed, which means a new socket instance has to be created. 
      socket.close(); 
     } 

    } catch (Exception e) { 

     Log.e("TCP", "C: Error", e); 

    } 

} 

    //Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity 
    //class at on asynckTask doInBackground 
    public interface OnMessageReceived { 
    public void messageReceived(String message); 
} 

}

packet format數據包結構

分組格式包含, 雖然與設備上進行通信的代碼TCP,數據包之間的邊界未被識別,在這種情況下,如果數據包無序或者如果丟失了任何數據包,可以使用「標題開始」標識新的數據包。 因此,數據包中的前2個字節表示數據包的開始。

標題開始:指示每個分組開始的雙字節字段。 0x55AA是一個2字節的數字,用作標題開始。

協議版本:單字節字段,用於指定所用協議的版本。有效負載中指定的版本將決定有效負載結構。在任何時候,設備將支持單協議版本。目前的協議版本是'1'。

DSN:序列號是1字節的字段,它將唯一標識數據包。數據包的請求者將不得不填充請求有效載荷中的該字段;響應者必須在響應有效載荷中填充相同的唯一標識符。

請求ID:單字節字段指定命令ID。有效負載的解析將根據命令ID完成。在請求有效載荷的情況下,該字段將不爲零,並且在響應的情況下將爲零。

有效載荷長度:雙字節字段以字節爲單位指定有效載荷的長度。它指定了緊隨有效載荷長度字段的字節數。在有效載荷長度中,不包括標題長度和CRC。目前,網關設備支持的最大有效載荷長度爲512(字節)。 CRC:1個字節的字段,它將通過對所有字節進行異或並將XOR計數加0. 0

它正在工作。但根據文檔,我必須使用二進制通信協議發送數據包。包括標題開始,有效載荷數據,等等。我怎樣才能發送這些參數在數據包結構?我如何創建數據包?

任何幫助表示讚賞。

+0

廣東話份額文檔。只是想知道如何發送這些參數 –

+1

因爲你正在處理髮送字節,你(可能)會想要使用'DataInputStream'和'DataOutputStream'。我使用一個長度爲'7'的字節數組來通過'Payload Length'來存儲'Header Start'。然後另一個字節數組,最大長度爲((2^15)),以存儲「Payload Data」和「CRC」。然後,我將兩個數組連接成一個大小爲'7 +(2^15)'的字節數組,並使用'DataOutputStream#write(byte [] array,int offset,int length)'寫入該數組,'offset'爲'0','長度'爲'7 +(2^15)'。然而這是一個非常低水平的方法。 –

+0

你可以請用一些代碼發佈答案。那麼有效載荷數據呢?它會是什麼結構 –

回答

2

主要的錯誤是我沒有考慮原始數據類型的大小。

字節= 1字節

短= 2個字節

INT = 4個字節

長= 8個字節

浮子= 4個字節

雙= 8個字節

炭= 2字節

引用原始數據類型的大小後,我實現,因爲我們正在處理的字節數組我們應該跟蹤的大小和分組的索引。

TcpPacket.java

public class TcpPacket { 

private static int header_start = 0x55AA; 

private static int protocol_version = 1; 

private PacketUtils packetUtils = new PacketUtils(); 






public byte[] getHandshakePacket() 
{ 
    int request_id = 1; 

    byte[] header_data = packetUtils.ItoBA2(header_start); 
    byte[] payload_data = packetUtils.ItoBA4(packetUtils.getDateTime()); 
    byte[] payload_length = packetUtils.ItoBA2(4); 


    byte[] a_data = new byte[]{header_data[0], header_data[1], (byte) protocol_version, packetUtils.getDSN(), (byte) request_id, payload_length[0], payload_length[1], 
      payload_data[0], payload_data[1], payload_data[2], payload_data[3]}; 

    byte[] b_data = new byte[]{ packetUtils.getCRC(a_data)}; 

    byte[] packet_data = packetUtils.concatBytes(a_data,b_data); 


    return packet_data; 
} 

} 

PacketUtils.java

public class PacketUtils { 



public byte[] ItoBA4(int value) {  // integer to bytes function (return byte array of 4 bytes) 
    return new byte[] { 
      (byte)(value >>> 24), 
      (byte)(value >>> 16), 
      (byte)(value >>> 8), 
      (byte)value}; 
} 


public byte[] ItoBA2(int value) { // integer to bytes function (return byte array of 2 bytes) 
    return new byte[] { 
      (byte)(value >>> 8), 
      (byte)value}; 
} 


public byte getDSN() // return one byte random number 
{ 
    char[] chars = "1234567890".toCharArray(); 
    StringBuilder sb = new StringBuilder(); 
    Random random = new Random(); 
    for (int i = 0; i < 1; i++) { 
     char c = chars[random.nextInt(chars.length)]; 
     sb.append(c); 
    } 
    byte output = Byte.valueOf(sb.toString()); 
    return output; 
} 



public byte getCRC(byte[] packet) // required CRC function (return byte) 
{ 
    try 
    { 
     if (packet == null) 
     { 
      //Logger.Error("empty packet received"); 
      return (byte)0; 
     } 

     byte XORCheckSum = 0; 
     byte zeroCount = 0; 
     byte FFCount = 0; 

     for (int i = 0; i < packet.length; i++) 
     { 
      XORCheckSum ^= packet[i]; 
      if (packet[i] == (byte) 0) 
      { 
       zeroCount++; 
       continue; 
      } 
      if (packet[i] == (byte)255) 
      { 
       FFCount++; 
       continue; 
      } 
     } 

     XORCheckSum ^= zeroCount; 
     XORCheckSum ^= FFCount; 
     return XORCheckSum; 
    } 
    catch (Exception ex) 
    { 
     //Logger.Error(ex); 
     return (byte)0; 
    } 
} 



byte[] concatBytes(byte[]...arrays)  // concatenate byte arrays 
{ 
    // Determine the length of the result array 
    int totalLength = 0; 
    for (int i = 0; i < arrays.length; i++) 
    { 
     totalLength += arrays[i].length; 
    } 

    // create the result array 
    byte[] result = new byte[totalLength]; 

    // copy the source arrays into the result array 
    int currentIndex = 0; 
    for (int i = 0; i < arrays.length; i++) 
    { 
     System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length); 
     currentIndex += arrays[i].length; 
    } 

    return result; 
} 

public int getDateTime() 
{ 
    int dateInSec = (int) (System.currentTimeMillis()/1000); 
    return dateInSec; 

} 



}