2013-07-30 56 views
1

我試圖通過首先序列化它然後在另一端反序列化它來發送和通過udp對象。我認爲這將是微不足道的,因爲我之前已經通過udp發送了其他數據,並將這些數據序列化到文件等。在Java中通過udp發送對象

我已經調試了一些東西,並且在接收端不斷收到EOFException。數據包正常到達,但不知何故反序列化失敗。我不確定錯誤是發送者還是接收者。我想問題可能在於接收者不知道數據包的大小。

這是我的發送者類:

package com.machinedata.sensordata; 

import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.net.DatagramPacket; 
import java.net.DatagramSocket; 
import java.net.InetAddress; 

import android.content.Context; 
import android.util.Log; 

import com.machinedata.io.DataSerializer; 
import com.machinedata.io.ManagerUdpPacket; 

/** 
* This class sends udp-packets. It is used to send driver's information to the manager tablet. 
* @author tuomas 
* 
*/ 
public class UdpSender 
{ 
    private final int MANAGER_PORT = 1234; 
    private String ip = "192.168.11.50"; //tablet's IP 
    private DatagramSocket sock = null; 
    private InetAddress host; 
    private String mType; 
    private DataSerializer dataser; 

    public UdpSender(Context context) 
    { 
     try 
     { 
      sock = new DatagramSocket();  
      host = InetAddress.getByName(ip); //tabletin ip 
     } 
     catch(Exception e) 
     { 
      System.err.println("Exception alustettaessa senderia" + e); 
     } 

     dataser = new DataSerializer(context); 
    } 

    /** 
    * With this function we can send packets about our machine to the manager to 
    * see in the fleet-view. 
    */ 
    public void sendToManager(ManagerUdpPacket managerUdp) 
    { 

     //serialize 
     Log.v("sendudp", "Send a packet: " + managerUdp.getDriver()); 

     //serialize 
     byte[] data = dataser.serializeManagerPacket(managerUdp); 


     //send 
     try 
     { 
       DatagramPacket dp = new DatagramPacket(data , data.length , host , MANAGER_PORT); 
       sock.send(dp);  
     } 

     catch(IOException e) 
     { 
      System.err.println("IOException senderissa " + e); 
     } 


    } 

    public void close() 
    { 
     sock.close(); 
    } 
} 

這裏是串行化函數:

/** 
* Serializes packet to be sent over udp to the manager tablet. 
*/ 
public byte[] serializeManagerPacket(ManagerUdpPacket mp) 
{ 
    try 
    { 
     ByteArrayOutputStream baos = new ByteArrayOutputStream(2048); 
     ObjectOutputStream oos = new ObjectOutputStream(baos); 
     oos.writeObject(mp); 
     oos.close(); 
     // get the byte array of the object 
     byte[] obj= baos.toByteArray(); 
     baos.close(); 
     return obj; 
    } 
    catch(Exception e) { 
     e.printStackTrace(); 
    } 

    return null; 

} 

分組接收器類

public class UdpReceiver { 

private DatagramSocket clientSocket; 
private byte[] receiveData; 
private final int timeout = 1; 

/** 
* Create a receiver. 
* @param port Port to receive from. 
* @param signCount Number of signals in a packet 
*/ 
public UdpReceiver(int port) 
{ 

    //receiveData = serializeManagerPacket(new ManagerUdpPacket("asd", new MachineData(1, 2, "asd", "modelName"), 1,2,3,4,5.0,null)); 

    try{ 
     clientSocket=new DatagramSocket(port); 
     clientSocket.setReceiveBufferSize(2048); 
     clientSocket.setSoTimeout(timeout); 
    }catch(SocketException e){ 
     Log.e("ERR", "SocketException in UdpReceiver()"); 
    } 
} 

public void close() 
{ 
    clientSocket.close(); 
} 

/** 
* Receive a data packet and split it into array. 
* @param data Array to put data in, must be correct size 
* @return True on successful read, false otherwise 
*/ 
public ManagerUdpPacket receive() 
{ 

    //receive a packet 
    DatagramPacket recvPacket = new DatagramPacket(receiveData, receiveData.length); 
    try{ 
     clientSocket.receive(recvPacket); 
    }catch(IOException e){ 
     Log.e("ERR", "IOException in UdpReceiver.receive"); 
     return null; 
    } 

    ManagerUdpPacket obj = deserializeManagerPacket(receiveData); 

    if (obj != null) 
     Log.v("udpPacket", "UDP saatu: " + obj.getDriver()); 
    return obj; 
} 


/** 
* Deserialize the udp-packet back to readable data. 
* @param data 
* @return 
*/ 
public ManagerUdpPacket deserializeManagerPacket(byte[] data) 
{ 
    try 
    { 
     ObjectInputStream iStream = new ObjectInputStream(new ByteArrayInputStream(data)); 
     ManagerUdpPacket obj = (ManagerUdpPacket) iStream.readObject(); 
     iStream.close(); 
      return obj; 
     } 
     catch(Exception e) 
     { 
      e.printStackTrace(); 
     } 

     return null; 
    } 
} 

螺紋監聽包在接收端:

dataStreamTask = new TimerTask() 
    { 
     public void run() 
     { 
      if (currentlyStreaming) 
      { 

       ManagerUdpPacket mp = udpReceiver.receive(); 

       if(mp != null) 
       { 
        Log.v("log", "Paketti saatu! " + mp.getDriver()); 
       } 


       //stop thread until next query 
       try { 
        synchronized(this){ 
         this.wait(queryInterval); 
        } 
       } catch (InterruptedException e) { 
        Log.e("ERR", "InterruptedException in TimerTask.run"); 
       } 
      } 

     } 

最後類我送過UDP:

public class ManagerUdpPacket implements Serializable 
{ 
    private static final long serialVersionUID = 9169314425496496555L; 

    private Location gpsLocation; 
    private double totalFuelConsumption; 
    private long operationTime; 

    //workload distribution 
    private long idleTime = 0; 
    private long normalTime = 0; 
    private long fullTime = 0; 

private int currentTaskId; 
private String driverName; 
String machineModelName = ""; 
String machineName = ""; 
int machineIconId = -1; 
int machinePort = -1; 

public ManagerUdpPacket(String driver, MachineData machine, int currentTaskId, long idleTime, long fullTime, long operationTime, double fuelConsumption, Location location) 
{ 
    driverName = driver; 
    this.currentTaskId = currentTaskId; 
    this.idleTime = idleTime; 
    this.fullTime = fullTime; 
    this.operationTime = operationTime; 
    this.totalFuelConsumption = fuelConsumption; 
    this.gpsLocation = location; 
    machineModelName = machine.getModelName(); 
    machineName = machine.getName(); 
    machineIconId = machine.getIconId(); 
    machinePort = machine.getPort(); 
} 

public String getDriver() 
{ 
    return driverName; 
} 
public int getCurrentTaskId() 
{ 
    return currentTaskId; 
} 
public long getIdleTime() 
{ 
    return idleTime; 
} 
public long getFullTime() 
{ 
    return fullTime; 
} 
public long getOperationTime() 
{ 
    return operationTime; 
} 
public double getTotalFuelConsumption() 
{ 
    return totalFuelConsumption; 
} 
public double getLocation() 
{ 
    return gpsLocation.getLatitude(); 
} 
public String getMachineModelName() 
{ 
    return machineModelName; 
} 
public String getMachineName() 
{ 
    return machineName; 
} 
public int getMachineIconId() 
{ 
    return machineIconId; 
} 
    public int getMachinePort() 
    { 
     return machinePort; 
    } 


} 

我試圖從串行數據包的大小,將數據包的大小或基於互聯網上的一些例子中插入任意的2048。儘管如此,仍然無法實現。

+0

您的反序列化失敗,因爲您試圖反序列化部分字節流。只有在收到全部數據後才應調用反序列化。 – krishna

回答

2

據我所知接收函數返回它收到的字節的長度。但是,您的緩衝區將滿:

例子:

int buffersize = 1024;

您通過UDP發送8個字節。

所以你byte[]會充滿你的8個字節,但1024的其餘部分將是0

節省您在.receive()調用獲取規模和只是你緩衝區的所有值保存到另一個字節[],你應該得到你的對象。

對於示例:

public ManagerUdpPacket receive() 
{ 
int receivedBytes = 0; 

//receive a packet 
DatagramPacket recvPacket = new DatagramPacket(receiveData, receiveData.length); 
try{ 
    receivedBytes = clientSocket.receive(recvPacket); 
}catch(IOException e){ 
    Log.e("ERR", "IOException in UdpReceiver.receive"); 
    return null; 
} 
byte[] myObject = new byte[receivedBytes]; 

for(int i = 0; i < receivedBytes; i++) 
{ 
    myObject[i] = receiveData[i]; 
} 

ManagerUdpPacket obj = deserializeManagerPacket(myObject); 

if (obj != null) 
    Log.v("udpPacket", "UDP saatu: " + obj.getDriver()); 
return obj; 
} 
+0

您將接收到的數據寫入receiveData字節數組。所以這將與您收到的數據充分。但是,由於它是2048字節大,其餘的填充爲0.如果試圖反序列化這個字節數組,它將嘗試讀取整個2048字節。你必須剝離陣列。因此,您需要創建一個與接收到的數據一樣大的新字節數組(您可以通過讀取您接收到的數據量來知道)。現在可以反序列化此字節數組。 – Loki

+0

感謝您解釋和示例代碼!現在工作:)順便說一句,clientSocket.receive()是一個無效的功能,所以我不得不使用\t \t clientSocket.receive(recvPacket); receivedBytes = recvPacket.getLength();代替。以防萬一別人讀到答案和奇觀。 – Tumetsu

+0

噢...對不起,這個xD。 – Loki

1

當UDP接收數據時,總是使用java.net.DatagramSocket.getReceiveBufferSize();。這是套接字的實際大小或SP_RCVBUF。由於UDP是一種基於數據報的協議,與流協議TCP不同,因此接收緩衝區對於數據的完整性至關重要。通常情況下,接收緩衝區和發送緩衝區的大小相同,但在使用DatagramSocket.send(DatagramPacket)時您不會感到困擾,或者您也可以使用DatagramSocket.setSendBufferSize(DatagramSocket.getSendBufferSize())來爲此套接字使用SO_SNDBUF選項。請記住,在UDP中,如果使用大於平臺的SO_SNDBUF大小,則數據包可以被丟棄。