0

I want my Java program to communicate with a C program. This is just a simple example but I can't get it working. The Java program is supposed to run the C program and write to its input stream. The C program should see this and write to stdout in response. Finally, the Java program should read this response from the C program's stdout and print it to the screen.

Running the C program from command line I get the desired behaviour. However, when ran from the Java program, it just "hangs" and doesn't do anything. The Java program seems to have written its message to C program's stdin, but this message isn't seen in the C program.

I set the C program to write the message it reads to file just to check that it does read the message, and it doesn't do that.

Here is the C program:

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

void hello();
void wrong();

int main() {
    char buff[256];

    /* 1. read stdin */
    fscanf(stdin, "%s", buff);

    /* side effect - if message was received it should be 
        printed to file */
    FILE *fp = fopen("file.txt", "w");
    fprintf(fp, "%s", buff);
    fclose(fp);

    /* 2. depending on message, write something to stdout */
    if(strcmp(buff, "hello") == 0) {
        hello();
    } else {
        wrong();
    }
}

void hello() {
    printf("Hello World!");
}

void wrong() {
    printf("WRONG!");
}

And here's the Java program:

import java.io.*;

public class Main {
    public static void main(String[] args) {
        try {
            // 1. run C program
            Process proc = Runtime.getRuntime().exec("./hello");
            InputStream in = proc.getInputStream();
            OutputStream out = proc.getOutputStream();
            // 2. write 'hello' to 'hello' program
            writeToProc(out, "hello");
            // 3. read response
            readFromProc(in);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    // write message to process
    public static void writeToProc(OutputStream out, String msg) throws IOException {
        byte[] buff = msg.getBytes();
        out.write(buff);
        out.flush();
        System.out.println("done writing: " + new String(buff));
    }

    // read stdin of process
    public static void readFromProc(InputStream in) throws IOException {
        byte[] buff = new byte[256];
        int read = in.read();
        for(int i=0; read != -1; i++) {
            read = in.read();
            buff[i] = (byte) read;
        }
        String str = new String(buff);
        System.out.println("proc says: " + str);
    }
}

When I run Main, I get the following output:

$ java Main
  done writing: hello

And then just a blinking cursor and file "file.txt" is not written, indicating that the C program didn't read "hello" from stdin.

This is a simple example so I guess I'm missing something simple or coming at this the wrong way somehow.

Yulek
  • 331
  • 3
  • 12
  • 1
    First of all: is the problem with the C program or the Java program? Then get rid of the tag and the code for the one that's working. – Joe C Apr 23 '17 at 22:51
  • I don't know. I'm guessing the Java program but I'm not sure. – Yulek Apr 23 '17 at 22:52
  • Well, let's start with this: given that the Java program is calling the C program, does the C program work? – Joe C Apr 23 '17 at 22:53
  • Yeah. When I just run the C program from command line it runs fine. "file.txt" is written and then the correct message gets printed. – Yulek Apr 23 '17 at 22:54

1 Answers1

4

The problem is that you forgot to add the new line character \n to the string that you are sending from Java thus fscanf keeps waiting indefinitely for the process to "press enter". I've made some modifications to the code and the changes are documented in comments.

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class Main {
    public static void main(String[] args) {
        try {
            // 1. run C program
            Process proc = Runtime.getRuntime().exec("./hello");
            InputStream in = proc.getInputStream();
            OutputStream out = proc.getOutputStream();
            // 2. write 'hello' to 'hello' program
            // <change>
            // don't forget to add the new line here or in the 
            // writeToProc method
            writeToProc(out, "hello\n");
            // 3. read response
            readFromProc(in);
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    // write message to process
    public static void writeToProc(OutputStream out, String msg) throws IOException {
        // <change>
        // Using UTF-8 encoding since all chars in C are byte sized
        byte[] buff = msg.getBytes("UTF-8");
        out.write(buff);
        out.flush();
        System.out.println("done writing: " + new String(buff));
    }

    // read stdin of process
    public static void readFromProc(InputStream in) throws IOException {
        byte[] buff = new byte[256];
        int read = in.read();
        int index = 0;
        while(read != -1) {
            buff[index] = (byte) read;
            read = in.read();
            ++index;
        }
        String str = new String(buff, 0, index);
        System.out.println("proc says: " + str);
    }
}
StaticBeagle
  • 5,070
  • 2
  • 23
  • 34
  • You don't need the array copy if you use `new String(buff,0,index)` to convert only the valid part of the buffer. (Also the leftover elements contain the `byte` value 0 which is invisible when displayed, not `char '0'` which in ASCII compatible codes including Unicode is 0x30 aka 48.) – dave_thompson_085 Apr 27 '17 at 03:17
  • @dave_thompson_085 thanks for your input, I didn't think of the `new String(buff,0,index) ` constructor. Regarding the trailing zeroes, I thought they would be invisible as well but they showed up in eclipse, I do think that they might not show up in a "real" console though but didn't try it. I liked the `new String(buff,0,index)` solution and updated my answer to use that approach. Thanks! – StaticBeagle Apr 27 '17 at 04:38