【问题标题】:Javolution ByteBuffer questionJavolution ByteBuffer 问题
【发布时间】:2011-08-02 15:22:03
【问题描述】:

我有以下 Javolution 实现:

public class RunScan extends Struct
{
    public final Signed32 numOfClusters = new Signed32();
    public final ClusterData[] clusters;
    public final Signed32 numOfRecons = new Signed32();
    public final ReconData[] recons ;

    public RunScan (int numOfClusters, int numOfRecons)
    {
        this.numOfClusters.set(numOfClusters);
        this.numOfRecons.set(numOfRecons);
        clusters = array(new ClusterData[numOfClusters]);
        recons = array(new ReconData[numOfRecons]);      
    }
}

public class ClusterData extends Struct
{
    public final UTF8String scanType = new UTF8String(CommInterfaceFieldConstants.SCAN_TYPE_SIZE);
    public final UTF8String patientId = new UTF8String(CommInterfaceFieldConstants.PATIENT_ID_SIZE);
.
.
.
}

public class ReconData extends Struct
{
    public final UTF8String patientId = new UTF8String(CommInterfaceFieldConstants.PATIENT_ID_SIZE);
    public final UTF8String scanSeriesId = new UTF8String(CommInterfaceFieldConstants.SCAN_SERIES_ID_SIZE);
.
.
.
}

在我们的通信类中,在将数据放入套接字之前,我们需要获取 RunScan 对象的 bytes[],但是我们在 "//

private byte[] getCmdBytes(Struct scCmd)
    {
        ByteBuffer cmdBuffer = scCmd.getByteBuffer();
        int cmdSize = scCmd.size();

        byte[] cmdBytes = new byte[cmdSize];
        if (cmdBuffer.hasArray()) 
        {
            int offset = cmdBuffer.arrayOffset() + scCmd.getByteBufferPosition();
            System.arraycopy(cmdBuffer.array(), offset, cmdBytes, 0, cmdSize);            
        } 
        else 
        {
            String msg = "\n\ncmdBufferRemaining=" + cmdBuffer.remaining() + ", cmdBytesSize=" + cmdBytes.length + "\n\n";
            System.out.println(msg);
            cmdBuffer.position(scCmd.getByteBufferPosition());
            cmdBuffer.get(cmdBytes); //<<<<<<<<<< underFlowException         
        }

        return cmdBytes;
    }

此方法适用于其他情况。发生异常是因为这一行,

ByteBuffer cmdBuffer = scCmd.getByteBuffer();

我认为仅返回 RunScan 对象的 8 个字节(来自剩余()方法)的 ByteBuffer,它们是这两个 Signed32 字段。但是这一行,

int cmdSize = scCmd.size();

返回包含这两个数组大小的 RunScan 对象的正确长度。

如果我在用硬编码长度声明它们(而不是在构造函数中“新建”它们)时创建这两个数组,它可以正常工作,没有任何异常。

谁能帮我弄清楚我们的实现有什么问题?

【问题讨论】:

    标签: java bytebuffer javolution


    【解决方案1】:

    我的代码遇到了类似的情况。通常,对于当前的 Struct 对象,您不能将可变长度数组定义在与包含数组中元素数量的成员相同的结构中。

    试试这样的:

    public class RunScanHeader extends Struct
    {
        public final Signed32 numOfClusters = new Signed32();
        public final Signed32 numOfRecons = new Signed32();
    }
    
    public class RunScanBody extends Struct
    {
        public final ClusterData[] clusters;
        public final ReconData[] recons ;
    
        public RunScan (int numOfClusters, int numOfRecons)
        {
            clusters = array(new ClusterData[numOfClusters]);
            recons = array(new ReconData[numOfRecons]);      
        }
    }
    

    然后,您将需要一个两阶段的方法来读取和写入,首先读取/写入标头数据,然后读取/写入正文数据。

    对不起,我目前没有更多细节,如果你不能解决这个问题,请告诉我,我会重新挖掘我的代码。

    【讨论】:

    • 感谢您的帮助。我会测试一下我们的服务器端是否准备好了。
    • 作为一个通用的、非特定的库主题,在处理原始 byte[] 类型数据时,请考虑确保实现与客户端/服务器平台无关。例如,如果字节表示数字,则必须考虑字节序。一般来说,我对这个答案投了赞成票,因为它涉及(更高效/一致的)方法,首先发送一个指示即将到来的长度的标头,然后是(可变)数据包。这允许服务器读取固定的标头,然后动态分配内容并完全/有效地读取。
    【解决方案2】:

    初始化顺序很重要,它定义了每个字段的位置。在声明字段时完成初始化(最常见的情况)。或者,如果您在构造函数中执行此操作,则必须记住在成员初始化之后调用构造函数。这是在构造函数中完成初始化的示例:

     public class RunScan extends Struct {
         public final Signed32 numOfClusters;
         public final ClusterData[] clusters;
         public final Signed32 numOfRecons;
         public final ReconData[] recons ;
    
         public RunScan (int numOfClusters, int numOfRecons) {
            // Initialization done in the constructor for all members 
            // Order is important, it should match the declarative order to ensure proper positioning.
            this.numOfClusters = new Signed32();  
            this.clusters = array(new ClusterData[numOfClusters]);
            this.numOfRecons = new Signed32();
            this.recons = array(new ReconData[numOfRecons]);
    
            // Only after all the members have been initialized the set method can be used.
            this.numOfClusters.set(numOfClusters);
            this.numOfRecons.set(numOfRecons);
         }
    }
    

    【讨论】:

    • 感谢您的帮助。根据您的建议,可以避免 UnderflowException,但是当我将 ByteBuffer 转换为 byte[] 以便放入 Socket 时,我仍然无法从 ClusterData 数组中获得正确的值(numOfCluster 值很好。)。我已经尝试过 Jimn235 的建议,但还是一样。所以我必须编写自己的方法来手动创建一个 ByteBuffer 并将 RunScan 的所有内容复制到该 ByteBuffer 中,然后将该 ByteBuffer 转换为 byte[]。
    【解决方案3】:

    get() 将移动ByteBuffer 的位置。

    scCmd.getByteBuffer().slice().get(dest) 可能会解决您移动位置和意外副作用的问题。

    如果slice() 生成错误的原始缓冲区图片,scCmd.getByteBuffer().duplicate().get(dest) 也可能会解决您的问题。

    此外,scCmd.getByteBuffer() 似乎创建了一个冗余引用,并且您在同一方法中调用源引用和子引用。

    如果scCmd.getByteBuffer() 已经向您传递了slice(),那么您对这些方法的冗余访问肯定会做一些您计划之外的事情。

    【讨论】:

    • 您好 Jimn235,感谢您的建议。 Dautelle的回复请参考我的cmets。根据您的建议,我无法看到不同的结果。
    • 我对您的复制代码感兴趣,只是想看看它是否可以比您的描述更简单。如果没有整个代码库,就很难知道缓冲区周围的外观背后是什么。
    猜你喜欢
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多