截断DatagramPacket字节数组

时间:2014-09-01 18:28:02

标签: java sockets

我正在使用DatagramPacketDatagramSocket来制作客户端/服务器情况。我有一个简单的客户端和服务器的工作项目。客户端会将一个序列化的对象发送到服务器并且就是它。

下面显示的代码完美无缺。我将发布整个类,以防任何人想要运行代码。

我尝试做的是将发送的数据截断为恰好适合的数组(基于How to get rid of the empty remaining of the buffer?)。

当我打印出我发送的长度和我收到的长度时,我确实得到了这个输出:

GameClient: sent 330 bytes to the server
GameServer: received 330 bytes

我在接收方首先做的是:

        socket.receive(packet);
        // Deserialize the object.
        TestObject received = TestObject.deserialize(packet.getData());

但是,我已经将缓冲区大小设置为任意大小,所以我想我应该将实际字节放在适合该对象的字节数组中。所以我尝试了以下内容:

        socket.receive(packet);
        // Truncate the data into a smaller byte array.
        int actualSize = packet.getLength();
        byte[] actualPacket = new byte[actualSize];
        System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());

        // Deserialize the object.
        TestObject received = TestObject.deserialize(actualPacket); // Does not work.

但是,这段代码给了我一些预期

java.io.StreamCorruptedException: invalid stream header: 00000000

在第一行的deserialize TestObject方法中抛出:

        ObjectInputStream iStream = new ObjectInputStream(
                new ByteArrayInputStream(data));

这可能是什么问题?数据是一个精确的副本,对吗?

对象发送

package net;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class TestObject implements Serializable
{
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    public int value;
    public double anotherValue;
    public byte[] fillMe;

    public TestObject(int value)
    {
        super();
        this.value = value;
        fillMe = new byte[123];
    }

    public TestObject(int value, double anotherValue)
    {
        super();
        this.value = value;
        this.anotherValue = anotherValue;
    }

    public static byte[] serialize(TestObject o)
    {
        try
        {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(2048);
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            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 static TestObject deserialize(byte[] data)
    {
        try
        {
            ObjectInputStream iStream = new ObjectInputStream(
                    new ByteArrayInputStream(data));
            TestObject obj = (TestObject) iStream.readObject();
            iStream.close();
            return obj;
        } catch (Exception e)
        {
            e.printStackTrace();
        }

        return null;
    }
}

SERVER(接收端)

package net;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

import utils.Printer;
import engine.board.GameBoard;

public class GameServer extends Thread
{   private static int BUFFER_SIZE = 64000; //64k buffer
    private static final int SERVER_LISTENING_PORT = 1234;
    private DatagramSocket socket;
    private GameBoard game;

    public GameServer(GameBoard game)
    {
        this.game = game;
        try
        {
            this.socket = new DatagramSocket(SERVER_LISTENING_PORT);
        } catch (SocketException e)
        {
            e.printStackTrace();
        }
    }

    public void run()
    {
        while(true)
        {
            byte[] data = new byte[BUFFER_SIZE];
            DatagramPacket packet = new DatagramPacket(data,  data.length);
            try
            {
                socket.receive(packet);
                Printer.debugMessage(this.getClass(), String.format("received %s bytes", packet.getLength()));
            } catch (IOException e)
            {
                e.printStackTrace();
            }
            // Truncate the data into a smaller byte array.
            int actualSize = packet.getLength();
            byte[] actualPacket = new byte[actualSize];
            System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());

            // Deserialize the object.
            TestObject received = TestObject.deserialize(actualPacket); // Does not work.
            //TestObject received = TestObject.deserialize(packet.getData()); // Works fine?
            System.out.println("Server received object with value " + received.value);
        }
    }
}

客户(发送结束)

package net;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import utils.Printer;
import engine.board.GameBoard;

public class GameClient
{
    private static int BUFFER_SIZE = 64000;
    private static final int SERVER_LISTENING_PORT = 1234;
    private InetAddress serverIp;
    private DatagramSocket socket;
    private GameBoard game;
    private String clientName;

    public GameClient(String name, GameBoard game, String ipAddress)
    {
        this.game = game;
        this.clientName = name;
        try
        {
            this.socket = new DatagramSocket();
            this.serverIp = InetAddress.getByName(ipAddress);
        } catch (SocketException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (UnknownHostException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }


    public void sendData(byte[] data)
    {
        DatagramPacket packet = new DatagramPacket(data, data.length, serverIp, SERVER_LISTENING_PORT);

        try
        {
            socket.send(packet);
            Printer.debugMessage(this.getClass(), String.format("sent %d bytes to the server", data.length));;

        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}

TESTCODE

import net.GameClient;
import net.GameServer;
import net.TestObject;


public class Scratchpad
{
    static GameServer server;
    static GameClient client;
    static GameClient client2;
    public static void main(String[] args)
    {
        // Start the server
        server = new GameServer(null);
        server.start();

        // Init client (sender).
        client = new GameClient("client1", null, "localhost");

        // Create object to send.
        TestObject tester = new TestObject(1234);

        // Send the object.
        client.sendData(TestObject.serialize(tester));
        //client2.sendData("hello world".getBytes());
    }
}

1 个答案:

答案 0 :(得分:1)

你不需要任何这些。您可以从原始byte []数组中反序列化。它将在对象的末尾停止。最后的任何尾随事件都会被忽略。

但是,您可以大大简化新代码:

    TestObject received = TestObject.deserialize(packet.getData(), packet.getOffset(), packet.getLength());

并将您的反序列化方法更改为:

public static TestObject deserialize(byte[] data, int offset, int length)
{
    try
    {
        ObjectInputStream iStream = new ObjectInputStream(
                new ByteArrayInputStream(data, offset, length));
        // etc ...
}

我会进一步修改它以允许它抛出异常而不是默默地吞下它们。返回null是一项特别糟糕的策略,因为null是一个带内值,可能是您尝试传输的值。