使用 Java 在服务器应用程序和 Matlab 客户端之间进行套接字通信

Posted

技术标签:

【中文标题】使用 Java 在服务器应用程序和 Matlab 客户端之间进行套接字通信【英文标题】:Socket communication between server app and Matlab client using Java 【发布时间】:2012-01-25 17:59:18 【问题描述】:

我有一个编写的 C++ 服务器应用程序,我希望能够从 Matlab 控制它。到目前为止,我已经使用 mex 函数进行套接字通信,但我想放弃 mex 函数并直接在 m 文件中使用内联 Java。这将是一个更精简的解决方案。

我的基于 C++ 的独立应用程序需要按以下顺序包含以下数据的消息。 . .

这部分协议是固定的,不能更改:

uint32 magic_number - 这是一个幻数 (445566),必须在 消息的开头或消息的其余部分将被忽略。

uint32 num_bytes - 这是用于消息块其余部分的字节数 (不包括这最初的 8 个字节)

这部分协议是我设计的,可以修改:

接下来是由 4 个 uint8 值组成的标头(如 ipv4 地址) 向应用发出以下数据表示的信号(如果后面有任何数据)

在此之后,剩余的字节可以代表许多不同的东西。 最常见的是一个字符串(键值),后跟一长串浮点值(音频数据)。但是,可能只有一个字符串,或者它们可能只是一个浮点值数组。 4 个 uint8 值让服务器知道这里会发生什么。

如您所见,我目前正在将所有内容压缩到一个 uint8 数组中(一个巨大的组合)。这是因为 java“write”函数需要一个字节数组,而 Matlab uint8 数组是一种兼容的数据类型,正如我在 Mathworks 网站 Passing Data to a Java Method 上使用下表时发现的那样

我不是 Java 程序员,但今天下午我已经设法编写并运行了一段非常简单的通信代码。谁能帮我把它做得更好?

import java.net.Socket
import java.io.*

mySocket = Socket('localhost', 12345);
output_stream   = mySocket.getOutputStream;
d_output_stream = DataOutputStream(output_stream);


data = zeros(12,1,'uint8');

%Magic key: use this combination of uint8s to make
% a uint32 value of = 445566 -> massive code-smell
data(1) = 126;
data(2) = 204;
data(3) = 6;

%Size of message block:
%total number of bytes in following message including header
%This is another uint32 i.e. (data(5:8))

data(5) = 4;

%header B: a group of 4 uint8s
data(9) = 1;
data(10) = 2;
data(11) = 3;
data(12) = 4;

%Main block of floats
%????


d_output_stream.write(data,0,numel(data));


pause(0.2);
mySocket.close;

我已经尝试发送一个由我想发送的数据的不同部分组成的 java 对象,但我不确定它们最终是如何在内存中排序的。在 C/C++ 中,很容易将不同的数据类型附加到连续的内存块中,然后发送。我有一种简单的方法可以在 Java 中做到这一点吗?我最终也想进行双向通信,但这可以等到现在。感谢阅读。

【问题讨论】:

当我在 Matlab 和 Java 之间发送数据时,我发送带有必要属性的 java 对象。也许你可以试试。 你能缩小“许多不同的事情”的范围吗?它的组件是否总是一个原始数字数组或其他可以直接表示为 Matlab 原始数组的东西?你需要从服务器取回数据吗? 你确定“消息块的大小”是一个字节吗?这会将您限制为 256 字节的消息。这是您自己设计的协议吗? 我已更新问题以涵盖这些问题中提出的一些观点。感谢您的关注。 在字符串(键值)后面跟着浮点数的情况下,服务器怎么知道字符串有多长,浮点数有多长?该 4 字节标头是否编码长度,或者您是否使用固定长度?您可能需要更多子标题。查看 MAT 文件格式 doco 以了解有关 Matlab 本身如何执行此操作的示例:mathworks.com/help/pdf_doc/matlab/matfile_format.pdf 【参考方案1】:

这里至少有两个不同的问题。一个是如何构建使用这样的协议的 Matlab 代码。另一个是他如何在你拥有的这个有线协议中表示可能复杂的数据。

就组织 Matlab 代码而言,您可以使用类以更结构化的方式组织消息,并使用typecast 将数字转换为字节。也许是这样的。这假设您的客户端和服务器具有相同的原始类型的本机表示,并忽略网络字节顺序 (htonl/ntohl)。

classdef learnvst_message
    %//LEARNVST_MESSAGE Message for learnvst's example problem
    %
    % Examples:
    % msg = learnvst_message;
    % msg.payload =  'Hello world', 1:100 
    % msg.payloadType = uint8([ 5 12 0 0 ]);  % guessing on this

    properties
        magicNumber = uint32(445566);
        payloadType = zeros(4, 1, 'uint8');  %// header B
        payload = ;
    end

    methods
        function out = convertPayload(obj)
        %//CONVERTPAYLOAD Converts payload to a single array of bytes
        byteChunks = cellfun(@convertPayloadElement, obj.payload, 'UniformOutput',false);
        out = cat(2, byteChunks:);
        end

        function out = marshall(obj)
        payloadBytes = convertPayload(obj);
        messageSize = uint32(4 + numel(payloadBytes)); %// ex first 8 bytes
        out.headerBytes = [
            typecast(obj.magicNumber, 'uint8') ...
            obj.payloadType ...
            typecast(messageSize, 'uint8')];
        out.payloadBytes = payloadBytes;
        end

        function sendTo(obj, host, port)
        m = marshall(obj);
        mySocket = Socket(host, port);
        d_output = mySocket.getOutputStream();
        d_output.write(m.headerBytes, 0, numel(m.headerBytes));
        d_output.write(m.messageBytes, 0, numel(m.messageBytes));
        mySocket.close();
        end

    end
end

function out = convertPayloadElement(x)
if isnumeric(x)
    out = typecast(x, 'uint8');
elseif ischar(x)
    % Assumes receiver likes 16-bit Unicode chars
    out = typecast(uint16(x), 'uint8');
else
    % ... fill in other types here ...
    % or define a payload_element class that marshalls itself and call
    % it polymorphically
    error('Unsupported payload element type: %s', class(x));
end
end

我认为更具可读性,代码味道也少了一点。作为调用者,您可以以更结构化的形式处理数据,并将转换封装到类的编组方法内的有线协议字节。 “convertPayload”是“将由许多不同数据类型组成的通用内存块拼接在一起”。在 Matlab 中,uint8 数组是一种将不同数据类型的表示附加到一个连续的内存块中的方法。它基本上是unsigned char [] 的包装器,具有自动重新分配功能。 typecast(...,'uint8') 相当于在 C/C++ 中重新解释转换为 char *。查看它们的帮助。

但这会带来更多问题。服务器如何知道有效载荷的每个组件有多长,如果是多维的,它们的形状是什么,以及它们各自的类型是什么?或者如果它们是复杂的数据类型——它们可以嵌套吗?您可能需要在每个有效负载元素中嵌入小标题。上面的代码假设 4 字节的有效载荷类型标头完全描述了有效载荷内容。

听起来您正在寻找的可能是基于异构数组的数据的一种自描述格式。有现有的格式,包括 NetCDF、HDF5 和 Matlab 自己的 MAT 文件。 Matlab 内置了对它们的支持,或者您可以为它们引入第三方 Java 库。

就速度而言 - 每次通过 Matlab/Java 边界传递数据时,您都需要付费。大型原始数组的转换成本相对较低,因此您可能希望在将消息传递给 Java 之前将大部分消息打包到 Matlab 中的字节数组中,而不是进行大量单独的 write() 调用。在实践中,这取决于您的数据有多大和多复杂。请参阅Is MATLAB OOP slow or am I doing something wrong? 了解有关某些 Matlab 操作(包括 Java 调用)成本的粗略概念。 (完全披露:这是一个自插入。)

【讨论】:

哇!感谢您如此有见地的回复,并感谢您抽出宝贵的时间。现在已经很晚了,所以我会在早上进行实验,将它与我当前的 mex 解决方案进行基准测试,然后发布结果。我一定会阅读您提到的现有格式。再次感谢! 很高兴为您提供帮助;我不会经常使用这种低级的 Matlab 东西。 (这很好 - 每百万行代码中有一个 typecast() 听起来是正确的。)不要忘记使用 profile 来查看时间花在了哪里。祝你好运。 呃,“低级 Matlab”听起来像是矛盾的说法,是的。我说我要睡觉了,但我撒了谎,不得不试一试。 .使用示例代码时出现错误第一个输入参数必须是完整的、非复杂的数值。 ==> Janke>@(x)typecast(x,'uint8') 在 20 byteChunks = cellfun(@(x) typecast(x, 'uint8'), obj.payload); ......现在肯定要睡觉了。再次感谢。 你是对的。也充满了其他错误。我重构了代码,以便可以对其进行测试(无需设置套接字)并修复了其中的一些。第一个极端情况——字符不能直接类型转换。 好的,在解决了一些无关紧要的错误之后,我设法让它运行良好。我通过向我的应用程序 + 标题信息发送 100,000 个单精度音频数据样本进行了测试。 mex 解决方案平均需要 9 毫秒,Matlab 解决方案也平均约为 9 毫秒。这个结果是出乎意料的,我很高兴!我会让代码更漂亮一点,然后稍后发布我的最终解决方案。再次感谢!

以上是关于使用 Java 在服务器应用程序和 Matlab 客户端之间进行套接字通信的主要内容,如果未能解决你的问题,请参考以下文章

使用 MatLab2013a 的 MacOS 10.8.4 上的 Java 错误

链接 AnyLogic 和 Matlab

使用 JDBC 驱动程序连接 MATLAB 和 MySQL

IPPL 与 Matlab 编译器运行时 (MCR)

如何使用java捕获某些应用程序的屏幕?

在 Java 中使用 Matlab 项目