Home > Blockchain >  Communicate Java and C without Sockets?
Communicate Java and C without Sockets?

Time:06-19

I was tasked to write a service in C that listens to a named or unnamed Windows OS pipe (two way data transfer should be possible) and the "host application" that writes data to the OS pipe should be a java program. I am not allowed to use sockets (for performance reasons) or any external non-default library.

Is it possible to write data to named/unnamed OS pipes with Java (if possible without turning the program into C code with JNI?) I've been thinking of running a CMD script that writes to an OS pipe with java but I'm pretty sure that it would be extremely inefficient. I've also looked into the java Pipe class but if I understood correctly, this is only for inter-thread communication inside the JVM and not a "real" OS pipe. How exactly can I write data to a "real" OS pipe? Performance is very important for this because I need a Java program to communicate with a C program, which runs CUDA operation so I can't afford to waste a lot of performance on sending/receiving the data.

CodePudding user response:

There's other answers lurking on StackOverflow that suggest that Java on Windows doesn't do Windows' pipes (see this one).

This restriction actually isn't that uncommon. ZeroMQ (another good candidate that does a very good job of abstracting IPC / sockets / etc) doesn't support IPC on Windows either. Cygwin - think Posix / Unix environment for Windows - does use Windows pipes for posix pipes, but they had to commit all sorts of horrid coding sins to do it (specifically, recreating a select() that supported Windows IPC / pipes meant starting a thread per "file descriptor" to poll the underlying Windows object - like going back to *nix in the 1980s).

In principal you could recompile a *nix JVM source code bundle for Cygwin, and then java code run in that would be able to access a Windows pipe. But that sounds really horrid. However, so long as you didn't need to use select() or epoll() to block on reading the pipe, it might end up being fairly fast and efficient.

EDIT

Franck has found otherwise! I'd be interested to know whether or not there's an implementation of java nio channels SelectorProvider that works in the Windows JVM for pipes.

CodePudding user response:

Requirements:

  • Java caller program calls a C calculator program running in its own process
  • Java sends operations
  • C program returns results
  • Avoid JNI/JNA if possible

The easiest way is probably to call the C program from Java and communicate via standard I/O. The C program could get the operations via stdin in a loop, do the calculations, and return the results via stdout.

In Java, the central mechanism for this would be the Process class. Oracle's documentation states:

Process provides control of native processes started by ProcessBuilder.start ... By default, the created process does not have its own terminal or console. All its standard I/O (i.e. stdin, stdout, stderr) operations will be redirected to the parent process, where they can be accessed via the streams obtained using the methods getOutputStream(), getInputStream(), and getErrorStream().

To avoid a deadlock, we alternately write a line with the operation to be performed and read back a line with the result.

The advantage of using stdin and stdout is of course also the manual ad-hocs testability since you can simply call the C program on the command line. By the way, the approach would also work in the same way under Linux and macOS.

To get as simple a test case as possible, here is a C program that gets either an addition or a subtraction operator with two arguments, calculates the result and returns it. Of course, the whole thing doesn't include CUDA and only minimal error handling and probably doesn't meet the detailed requirements, but it could be a start in the right direction.

The Java demo program finally call this C program and sends an addition and subtraction operation to the C program and outputs the returned results accordingly. The q command then terminates the C program.

Java

package com.software7.test;

import java.io.*;

public class Caller {

    public static void main(String[] args) {
        Caller caller = new Caller();
        caller.runCCalculator();
    }

    private void runCCalculator() {
        try {
            String[] command = { "C:\\Users\\stephan\\source\\repos\\CCalculator\\x64\\Debug\\CCalculator.exe" };
            ProcessBuilder processBuilder = new ProcessBuilder(command);
            Process process = processBuilder.start();

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
            BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String[] ops = {"  1.1 2.2", "- 1024.123 512.123"};
            for (String op : ops) {
                bw.write(op);
                bw.write("\n");
                bw.flush();
                String result = br.readLine();
                if (result != null) {
                    System.out.println(op   " = "   result);
                }
            }
            bw.write("q\n");
            bw.flush();

            bw.close();
            br.close();
            System.out.println("Process exited with "   process.waitFor());
        } catch (IOException | InterruptedException exp) {
            exp.printStackTrace();
        }
    }

}

C

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static void error_exit(const char *msg);
static char readOp(const char *token);
static double readArg(const char *token);

int main(void) {
    char buf[128];
    char *ptr;

    while ((ptr = fgets(buf, sizeof(buf), stdin)) != NULL) {
        buf[strcspn(buf, "\n")] = 0;
        if (strcmp(ptr, "q") == 0) {
            break;
        }
        char *token = strtok(ptr, " ");
        char op = readOp(token);
        token = strtok(NULL, " ");
        double val1 = readArg(token);
        token = strtok(NULL, " ");
        double val2 = readArg(token);

        switch (op) {
            case ' ':
                printf("%lf\n", val1   val2);
                fflush(stdout);
                break;
            case '-':
                printf("%lf\n", val1 - val2);
                fflush(stdout);
                break;
            default:
                error_exit("unknown operator");
        }
    }
    return 0;
}

static char readOp(const char *token) {
    if (token != NULL) {
        return *token;
    } else {
        error_exit("no operator");
    }
}

static double readArg(const char *token) {
    double res;
    if (token != NULL) {
        char *end_ptr;
        res = strtod(token, &end_ptr);
        if (token == end_ptr || *end_ptr != '\0') {
            error_exit("invalid float operand");
        }
    } else {
        error_exit("invalid operand");
    }
    return res;
}

static void error_exit(const char *msg) {
    fprintf(stderr, "%s\n", msg);
    exit(1);
}

Result

  1.1 2.2 = 3.300000
- 1024.123 512.123 = 512.000000
  • Related