Java中命名管道的并发读/写(在Windows上)

我正在尝试使用命名管道在Windows上提供C#应用程序和Java应用程序之间的通信,该管道使用v01ver在此问题中描述的方法: 如何从Java打开Windows命名管道?

我在Java方面遇到了一个问题,因为我有一个读者线程经常在管道上等待输入,当我尝试从我的主线程写入管道时,它会永远被卡住。

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(); } 

输出是:

 写一条消息...... 

然后它会永远等待。

如何在另一个线程中等待输入时写入命名管道?

这是管道的预期行为。 它应该挂起,直到其他进程连接到管道并读取它。

我想这里的RandomAccessFile不是正确的API。 在Java端尝试FileInputStream + FileOutputStream。 但这只是一个猜测,因为我上次使用Windows API时命名管道尚不存在。

我有同样的问题 – 使用命名管道在C#/ Python应用程序和Windows上的Java应用程序之间进行通信:

我们String echoResponse = pipe.readLine(); Java编写的客户端代码示例,但是在行中String echoResponse = pipe.readLine(); 踩等待永远。

 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(); } 

问题的解决方案:我从这里用Python编写的ServerPipe代码示例代码 – 命名管道 :并在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() 

服务器启动后,您可以使用稍微修改过的Java客户端代码版本:

 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(); } 

它在NetBeans 6.9.1中运行良好。

不用担心,使用RandomAccessFile访问命名管道是正确的。 命名管道是文件系统对象。 在Linux / Unix下,它也被称为“fifo”。 这些对象就像文件一样可读。 (与Java Pipe类抽象的进程之间使用的管道不同)。

但是我发现你的程序存在两个问题。 我目前无法测试它,因为我需要你的测试服务器(随意发布)。 您的读者线程等待来自另一方(即服务器)的答案。 它使用readLine(),我会使用不同的方法(用于调试char by char可能是最好的)。

使用Java(没有JNI),您实际上无法创建命名管道(服务器端)。 使用RandomAccessFile使用的generics方法打开命名管道,您将获得一个字节类型的流,可以是单向或双向的。

BTW:JTDS(SQL Server的免费JDBC驱动程序)可以选择使用命名管道访问SQL服务器,甚至可以通过网络访问。 它正是使用RandomAccessFile方法。

BTW2:旧的MS SQL Server安装媒体上有一个makepipe.exe测试服务器,但是我没有找到一个可靠的来源来获取该文件。

我对JAVA并不熟悉,而且我的C#也非常基础。 但是,我遇到了类似的multithreadingC ++客户端问题,我通过打开重叠IO管道来解决这个问题。 直到我这样做,Windows序列化读取和写入,有效地导致不满意(阻塞)ReadFile,以防止在读取完成之前完成后续的WriteFile。

请参见CreateFile函数
FILE_FLAG_OVERLAPPED