java - 截断 DatagramPacket 字节数组

标签 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

在第一行 TestObjectdeserialize 方法中抛出:

        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();
        }
    }
}

测试代码

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());
    }
}

最佳答案

你不需要这些。您只需从原始 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 是一个带内值,可能是您尝试传输的值。

关于java - 截断 DatagramPacket 字节数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25611248/

相关文章:

java - 如何在 spring 中配置 Autowiring

java - Activity ActionBar 在 fragment 中不可见

linux - 监听Socket并复制到另一个端口

java - 明确转换: java时发生未知错误

java - Eclipse 默认在哪里寻找要使用的 JDK/JRE?

java - 关闭 ServerSocket - 如何正确检查客户端套接字是否已关闭?

java - Java 套接字数组

winapi - 是否可以在Reactor风格的操作中使用IOCP(或其他API)?

sockets - Linux 上的 ZeroMQ 和 TCP 重传

java - Oxygene 中的 Java 表达式 "Class.class"是什么?