4
votes

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?

5
...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 - bestsss
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. - Lawrence Dol
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. - Peter Lawrey
@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. - Chris Dickson
@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. - bestsss

5 Answers

3
votes

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

2
votes

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.

1
votes

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.

0
votes

Don't worry, using RandomAccessFile to access a named pipe is correct. A named pipe is a file system object. Under Linux/Unix it is also called "fifo". Those objects are readable just like a file. (and not the same as pipes used between processes which are abstracted by Java Pipe class).

However I see two problems with your program. I cannot test it currently as I would need your test server (feel free to publish). Your reader thread waits for answers from the other side (i.e. the server). It uses readLine(), I would use a different method (for debugging reading char by char might be the best).

With Java (without JNI) you cannot actually create a named pipe (server side). Opening a named pipe with the generic method used by RandomAccessFile you will get a byte-type stream which can be one-way or duplex.

BTW: JTDS (the free JDBC driver for SQL Server) can optionally use a named pipe to access SQL server, even over the network. And it is using exactly the RandomAccessFile method.

BTW2: there is a makepipe.exe test server on older MS SQL Server installation media, however I did not find a trusted source to get that file.

-1
votes

I'm not familiar with JAVA, and my C# is pretty elementary too. However I'm had a similar problem with a multithreaded C++ client that I fixed by opening the pipe for overlapped IO. Until I did this, Windows serialized reads and writes, effectively causing an unsatisfied (blocking) ReadFile to prevent completion of a subsequent WriteFile until the read was done.

See CreateFile function
FILE_FLAG_OVERLAPPED