mardi 21 juin 2016

IPC for Java programs using Windows' Named Pipes using JNI

I am creating a Java class using JNI that allows various IPC mechanisms between separate Java programs.

I've created a class called WindowsIPC that contains a native method that can access Windows' named pipes. I have a native function called createNamedPipeServer() that calls CreateNamedPipe. It appears to have created the pipe correctly as I can view it using a tool such as Process Explorer.

My problem is that when I make use of this in a separate Java program and making use of a separate thread to read and write data using Java's standard Input and output streams, it fails. I can write data to the pipe successfully but cannot read the contents; it returns with a FileNotFoundException (All pipe instances are busy).

I am struggling to wrap my head around this as I cannot figure out what other process is using the pipe as well as the fact that I've specified PIPE_UNLIMITED_INSTANCES when the pipe was created. I have read up extensively on how reads work and my hunch is that input/output streams in Java handle it due to the fact that it is returning the error mentioned above.

Any insights would be greatly appreciated.

Here is the code:

WindowIPC.java

public class WindowsIPC {
  public native int createNamedPipeServer(String pipeName);
  static {
    System.loadLibrary("WindowsIPC");
  }
  public static void main(String[] args) {
    // some testing..
  }
}

WindowsIPC.c

const jbyte *nameOfPipe; // global variable representing the named pipe
HANDLE pipeHandle; // global handle..

JNIEXPORT jint JNICALL Java_WindowsIPC_createNamedPipeServer
  (JNIEnv * env, jobject obj, jstring pipeName) {
    jint retval = 0;
    char buffer[1024]; // data buffer of 1K
    DWORD cbBytes;

    // Get the name of the pipe
    nameOfPipe = (*env)->GetStringUTFChars(env, pipeName, NULL);

    pipeHandle = CreateNamedPipe (
      nameOfPipe,                      // name of the pipe
      PIPE_ACCESS_DUPLEX,
      PIPE_TYPE_BYTE |
      PIPE_READMODE_BYTE |
      PIPE_NOWAIT,                    // forces a return, so thread doesn't block
      PIPE_UNLIMITED_INSTANCES,
      1024,
      1024,
      0,
      NULL
    );

    // error creating server
    if (pipeHandle == INVALID_HANDLE_VALUE) retval = -1;
    else printf("Server created successfully: name:%sn", nameOfPipe);

    // waits for a client -- currently in ASYC mode so returns immediately
    jboolean clientConnected = ConnectNamedPipe(pipeHandle, NULL);

    (*env)->ReleaseStringUTFChars(env, pipeName, nameOfPipe);
    return retval;
}

And finally TestWinIPC.java

import java.io.*;
import java.util.Scanner;
public class TestWinIPC {
  public static void main (String[] args)
    {

      WindowsIPC winIPC = new WindowsIPC();

      // TEST NAMED PIPES
      final String pipeName = "\\.\Pipe\JavaPipe";

      if (winIPC.createNamedPipeServer(pipeName) == 0) {
        System.out.println("named pipe creation succeeded");
        Thread t = new Thread(new NamedPipeThread(pipeName));
        t.start();
        try {
          System.out.println("opening pipe for input");
          BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pipeName)));
          System.out.println("waiting to read");
          String line = br.readLine();
          System.out.println("Read from pipe OK: " + line);
          br.close();
        }
        catch (IOException exc) {
          System.err.println("I/O Error: " + exc);
          exc.printStackTrace();
        }

      }
} //main

private static class NamedPipeThread implements Runnable {
    private String pipeName;

    public NamedPipeThread (String pipeName) {
      this.pipeName = pipeName;
    } // constructor

    public void run () {
     try {
         PrintWriter pw = new PrintWriter(new FileOutputStream(pipeName));
         pw.println("Hello Pipe");
         System.out.println("Wrote to named pipe OK");
         pw.close();
      }
      catch (IOException exc) {
          System.err.println("I/O Error: " + exc);
          exc.printStackTrace();
      }
    } // run
  } 
}

Aucun commentaire:

Enregistrer un commentaire