Concurrent read/write of named pipe in Java (on windows)

14,436

Solution 1

This is expected behaviour of pipes. It is supposed to hang untill other process connects to the pipe and reads it.

Solution 2

I have a same problem -- communication between a C#/Python app and a Java app on windows using named pipes:

We have example of Client Code written on Java, but in line String echoResponse = pipe.readLine(); tread waits forever.

try {
    // Connect to the pipe
    RandomAccessFile pipe = new RandomAccessFile("\\\\.\\pipe\\testpipe", "rw");
    String echoText = "Hello word\n";
    // write to pipe
    pipe.write ( echoText.getBytes() );
    // read response
    String echoResponse = pipe.readLine();
    System.out.println("Response: " + echoResponse );
    pipe.close();

    } catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

Solution of problem: I have a ServerPipe code written on Python from here Example Code - Named Pipes: and run its on Python 2.6.6

from ctypes import *

PIPE_ACCESS_DUPLEX = 0x3
PIPE_TYPE_MESSAGE = 0x4
PIPE_READMODE_MESSAGE = 0x2
PIPE_WAIT = 0
PIPE_UNLIMITED_INSTANCES = 255
BUFSIZE = 4096
NMPWAIT_USE_DEFAULT_WAIT = 0
INVALID_HANDLE_VALUE = -1
ERROR_PIPE_CONNECTED = 535

MESSAGE = "Default answer from server\0"
szPipename = "\\\\.\\pipe\\mynamedpipe"


def ReadWrite_ClientPipe_Thread(hPipe):
    chBuf = create_string_buffer(BUFSIZE)
    cbRead = c_ulong(0)
    while 1:
        fSuccess = windll.kernel32.ReadFile(hPipe, chBuf, BUFSIZE,
byref(cbRead), None)
        if ((fSuccess ==1) or (cbRead.value != 0)):
            print chBuf.value
            cbWritten = c_ulong(0)
            fSuccess = windll.kernel32.WriteFile(hPipe,
                                                 c_char_p(MESSAGE),
                                                 len(MESSAGE),
                                                 byref(cbWritten),
                                                 None
                                                )
        else:
            break
        if ( (not fSuccess) or (len(MESSAGE) != cbWritten.value)):
            print "Could not reply to the client's request from the
pipe"
            break
        else:
            print "Number of bytes written:", cbWritten.value

    windll.kernel32.FlushFileBuffers(hPipe)
    windll.kernel32.DisconnectNamedPipe(hPipe)
    windll.kernel32.CloseHandle(hPipe)
    return 0

def main():
    THREADFUNC = CFUNCTYPE(c_int, c_int)
    thread_func = THREADFUNC(ReadWrite_ClientPipe_Thread)
    while 1:
        hPipe = windll.kernel32.CreateNamedPipeA(szPipename,
                                                 PIPE_ACCESS_DUPLEX,
                                                 PIPE_TYPE_MESSAGE |
                                                 PIPE_READMODE_MESSAGE
|
                                                 PIPE_WAIT,

PIPE_UNLIMITED_INSTANCES,
                                                 BUFSIZE, BUFSIZE,

NMPWAIT_USE_DEFAULT_WAIT,
                                                 None
                                                )
        if (hPipe == INVALID_HANDLE_VALUE):
            print "Error in creating Named Pipe"
            return 0

        fConnected = windll.kernel32.ConnectNamedPipe(hPipe, None)
        if ((fConnected == 0) and (windll.kernel32.GetLastError() ==
ERROR_PIPE_CONNECTED)):
            fConnected = 1
        if (fConnected == 1):
            dwThreadId = c_ulong(0)
            hThread = windll.kernel32.CreateThread(None, 0,
thread_func, hPipe, 0, byref(dwThreadId))
            if (hThread == -1):
                print "Create Thread failed"
                return 0
            else:
                windll.kernel32.CloseHandle(hThread)
        else:
            print "Could not connect to the Named Pipe"
            windll.kernel32.CloseHandle(hPipe)
    return 0


if __name__ == "__main__":
    main()

After server have start you can use slightly modified version of the Java Client code:

try {
    // Connect to the pipe
    RandomAccessFile pipe = new RandomAccessFile("\\\\.\\pipe\\mynamedpipe", "rw");
    String echoText = "Hello world\n";
    // write to pipe
    pipe.write(echoText.getBytes());

    //String aChar;
    StringBuffer fullString = new StringBuffer();

    while(true){
        int charCode = pipe.read();
        if(charCode == 0) break;
        //aChar = new Character((char)charCode).toString();
        fullString.append((char)charCode);
    }

    System.out.println("Response: " + fullString);
    pipe.close();
}
catch (Exception e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

It works well in NetBeans 6.9.1.

Solution 3

I suppose that RandomAccessFile is not the right API here. Try a FileInputStream + FileOutputStream on the Java side. But that is only a guess, as I last used the Windows API in times when named pipes didn't yet exist.

Share:
14,436

Related videos on Youtube

takteek
Author by

takteek

[This information is available on a subscription basis only.]

Updated on June 04, 2022

Comments

  • takteek
    takteek almost 2 years

    I'm trying to provide communication between a C# app and a Java app on windows using named pipes with the method described by v01ver in this question: How to open a Windows named pipe from Java?

    I'm running into a problem on the Java side because I have a reader thread constantly waiting for input on the pipe and when I try to write to the pipe from my main thread it gets stuck forever.

    final RandomAccessFile pipe;
    try {
       pipe = new RandomAccessFile("\\\\.\\pipe\\mypipe", "rw");
    }
    catch (FileNotFoundException ex) {
       ex.printStackTrace();
       return;
    }
    
    Thread readerThread = new Thread(new Runnable() {
       @Override
       public void run() {
          String line = null;
          try {
             while (null != (line = pipe.readLine())) {
                System.out.println(line);
             }
          }
          catch (IOException ex) {
             ex.printStackTrace();
          }
       }
    });
    readerThread.start();
    
    try { Thread.sleep(500); } catch (InterruptedException e) {}
    
    try {
       System.out.println("Writing a message...");
       pipe.write("Hello there.\n".getBytes());
       System.out.println("Finished.");
    }
    catch (IOException ex) {
       ex.printStackTrace();
    }
    

    The output is:

    Writing a message...
    and then it waits forever.

    How can I write to a named pipe while waiting for input in another thread?

    • bestsss
      bestsss about 13 years
      ...and you cannot switch off and not switch off reading? read only if the file is not at the end, otherwise block on poll a blocking queue to wait for writes; and ultimately use a single thread. if you are interested i can show a snippet; however I have no xp w/ named pipes like that and a simple socket is 10 times easy to manage
    • Lawrence Dol
      Lawrence Dol about 13 years
      It might help to use JVisualVM to see whether you thread(s) are blocked at the O/S level or on a Java synchronization acquisition.
    • Vishy
      Vishy about 13 years
      Rather than using a named pipe, you might find using sockets is more scalable (named pipes are implemented using sockets in Windows anyway) You will find more examples of how to use them (as they used more often) which should help you.
    • Chris Dickson
      Chris Dickson about 13 years
      @Peter Lawrey: Named pipes in Windows are NOT implemented using sockets. When both client and server are on the same machine, they use shared memory for IPC and are extremely fast.
    • Vishy
      Vishy about 13 years
      @Chris, I must have been reading bad/old information. Has this always been the case in Windows? Using them in Java on Vista doesn't appear to be faster.
    • bestsss
      bestsss about 13 years
      @Peter, Java pipes (nio ones) are implemented via sockets on Windows and with OS pipes on Linux. Windows native named pipes are not sockets and their impl. does not depend on winsock. It's just java that doesn't use them since it's not possible to register the same selector for sockets and windows pipes.
    • Mayank Varshney
      Mayank Varshney over 11 years
      This is the link which you can use for Named pipe implementation : sonalimendis.blogspot.in/2010/10/…
  • Lawrence Dol
    Lawrence Dol about 13 years
    Yes, RandomAccessFile is completely the wrong abstraction for a pipe.
  • bestsss
    bestsss about 13 years
    ŭlo, you can not open file w/ 2 separate descriptors for both read and write. RadndomAccessFile is the way to go if you need both read and write at the same time.
  • Prashant
    Prashant over 11 years
    Well, if your Server write a message which is null-byte terminated it is no wonder that the readLine() function does not return. It is waiting for "\n\r" from your server. I would also not use the read() method in production code. Use a byte[] barr = new byte[1024]; count = pipe.read(barr); method in a look and make sure to not expect the whole message from the server in a single return.
  • Paŭlo Ebermann
    Paŭlo Ebermann over 11 years
    I don't really see how one could do random access with a pipe, could you add some example code?
  • Prashant
    Prashant over 11 years
    You cannot do random access (only skip of course), but still the class is the one to read and write from the named pipe.
  • Martin
    Martin over 11 years
    Java doesn't have any easy way of passing specific flags to CreateFile (unless you do a bunch of JNI).
  • user207421
    user207421 almost 7 years
    This is not an answer. It should have been posted as a separate question.