C++/Matlab Inter-Process Communication (IPC)
In a project I'm currently working on together with a colleague, we wanted to enable inter-process communication between a C++ program and a Matlab program. At first we thought of using TCP/IP directly for communication. However, TCP is a stream-oriented protocol and we wanted to avoid having to do message framing of the stream data ourselves. Thus, we decided to use ZeroMQ with the request-reply pattern instead for the transmission of JSON-encoded messages.
However, when setting up ZeroMQ for use with Matlab, we faced some difficulties. In order to make it easier for others to use ZeroMQ with Matlab for IPC with a C++ program in the future, I have written down below the steps that were necessary to get ZeroMQ to work with Matlab on an Ubuntu 20.04 system with Matlab 2020b.
First of all, we need to install Maven and the Java 8 SDK. Initially, I tried using Java 11, however, Matlab 2020b still does not seem to support it.
1sudo apt-get install maven libmaven-javadoc-plugin-java openjdk-8-jdk openjdk-8-jdk-headless
Then, one needs to download the latest release of JeroMQ, a Java implementation of ZeroMQ/libzmq. For example, the currently latest release is: https://github.com/zeromq/jeromq/archive/v0.5.2.tar.gz
After unpacking the source archive, switch to the source directory and execute the following commands.
1export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64/ 2mvn package
target/jeromq-0.5.2.jar to a directory where you'd like to store the compiled library, e.g., a subdirectory
libs/ in the folder containing your Matlab project. Then, you can execute your Matlab script using JeroMQ. Below, you can find example code for the request-reply pattern, which correctly handles program interrupts when the user presses CTRL+C.
1% BSD 2-Clause License 2% 3% Copyright (c) 2021, Christoph Neuhauser 4% All rights reserved. 5% 6% Redistribution and use in source and binary forms, with or without 7% modification, are permitted provided that the following conditions are met: 8% 9% 1. Redistributions of source code must retain the above copyright notice, this 10% list of conditions and the following disclaimer. 11% 12% 2. Redistributions in binary form must reproduce the above copyright notice, 13% this list of conditions and the following disclaimer in the documentation 14% and/or other materials provided with the distribution. 15% 16% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19% DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20% FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21% DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22% SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25% OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 27function zeromqReplier 28 % Either call the function javaclasspath below, or use: 29 % cd(prefdir) 30 % edit javaclasspath.txt 31 % -> Add a global path to the .jar file. 32 javaclasspath('libs/jeromq-0.5.2.jar') 33 34 import org.zeromq.SocketType; 35 import org.zeromq.ZMQ; 36 import org.zeromq.ZContext; 37 import java.lang.Thread 38 39 context = ZContext(); 40 socket = context.createSocket(SocketType.REP); 41 socket.bind('tcp://127.0.0.1:17384'); % arbitrary port 42 socket.setReceiveTimeOut(1000); 43 44 cleanupObj = onCleanup(@()cleanUpSocket(socket)); 45 46 while (~Thread.currentThread().isInterrupted()) 47 request_string = socket.recvStr(0); 48 49 if isempty(request_string) 50 continue; 51 end 52 53 request_string = request_string.toCharArray'; 54 55 % native2unicode(..., 'UTF-8') not necessary? 56 request = jsondecode(native2unicode(request_string(:)', 'UTF-8')); 57 %fprintf('Request string: %s\n', request_string); 58 disp(request); 59 60 % ... do something with the received request ... 61 62 % Send a reply. 63 reply = struct('filenames', 'test umlaut ü'); 64 reply_string = unicode2native(jsonencode(reply), 'UTF-8'); 65 %fprintf('Reply string: %s\n', reply_string); 66 disp(reply); 67 68 socket.send(reply_string, 0); 69 end 70 71 function cleanUpSocket(socket) 72 socket.close(); 73 end 74end