1. 程式人生 > >Java使用管道實現程序間通訊

Java使用管道實現程序間通訊

1.程序通訊

大家都知道程序間通訊有三種常用方式:

1)管道

2)共享記憶體

3)socket

baidu也有說8種方式的,其實基本都是這三種方式的進一步細化。

2.Java程序通訊

Java沒有共享記憶體機制,同時Java的管道也只能用於Java執行緒間的通訊。

下面是一個關於Java的管道簡單的介紹:

Java提供管道功能,實現管道通訊的類有兩組:PipedInputStream和PipedOutputStream或者是PipedReader和PipedWriter。管道通訊主要用於不同執行緒間的通訊。

一個PipedInputStream例項物件必須和一個PipedOutputStream例項物件進行連線而產生一個通訊管道。PipedOutputStream向管道中寫入資料,PipedIntputStream讀取PipedOutputStream向管道中寫入的資料。一個執行緒的PipedInputStream物件能夠從另外一個執行緒的PipedOutputStream物件中讀取資料。

看來Java只能通過Socket實現程序間通訊了。Socket本身確實具有很好的通用性,而且可以跨主機通訊。但是Scoket本身也有一些弱點,如效率相對其他方式較低,開發成本較高等。最重要的是,這篇文章我們要介紹的是Java如何使用PIPE實現程序間通訊。所以只好委屈一下Socket了。

3.Java通過管道實現程序通訊

3.1管道的本質

首先,我們需要說明一個基本概念:什麼是管道?

管道是Linux中很重要的一種通訊方式,是把一個程式的輸出直接連線到另一個程式的輸入,常說的管道多是指無名管道,無名管道只能用於具有親緣關係的程序之間,這是它與有名管道的最大區別。有名管道叫named pipe或者FIFO(先進先出),可以用函式mkfifo()建立。


Linux管道的實現機制



從本質上說,管道也是一種檔案,但它又和一般的檔案有所不同,管道可以克服使用檔案進行通訊的兩個問題,具體表現為:·
1)限制管道的大小。實際上,管道是一個固定大小的緩衝區。在Linux中,該緩衝區的大小為1頁,即4K位元組,使得它的大小不象檔案那樣不加檢驗地增長。使用單個固定緩衝區也會帶來問題,比如在寫管道時可能變滿,當這種情況發生時,隨後對管道的write()呼叫將預設地被阻塞,等待某些資料被讀取,以便騰出足夠的空間供write()呼叫寫。
2)讀取程序也可能工作得比寫程序快。當所有當前程序資料已被讀取時,管道變空。當這種情況發生時,一個隨後的read()呼叫將預設地被阻塞,等待某些資料被寫入,這解決了read()呼叫返回檔案結束的問題
注意:
從管道讀資料是一次性操作,資料一旦被讀,它就從管道中被拋 棄,釋放空間以便寫更多的資料。

管道的結構
在Linux 中,管道的實現並沒有使用專門的資料結構,而是藉助了檔案系統的file結構和VFS的索引節點inode。

過將兩個file 結構指向同一個臨時的VFS 索引節點,而這個VFS引節點又指向一個物理頁面而實現的。

3.2 Java如何使用管道通訊

上面說到,管道實際上就是一個file結構和一個VFS的索引。也就是說管道是一個虛擬的檔案。那在Java中是不是就可以通過直接讀寫這個檔案,從而實現PIPE通訊的效果呢?

答案當然是肯定的。其步驟如下:

1)首先要建立一個管道檔案,這一點Java 做不到,我們要藉助C/C++中的mkfifo()函式來實現。

以下程式碼建立並開啟兩個管道,一個用於讀取輸入,一個用於輸出:

	char name1[1024];
	char name2[1024];
	sprintf(name1,"./process_in");
	sprintf(name2,"./process_out");
	if(mkfifo(name1,0666 )<0)
	{
	 printf("\n error when mkfifo");	
	}
	if(mkfifo(name2,0666 )<0)
	{
	 printf("\n error when mkfifo");	
	}


	int fd1, fd2;
	if ((fd1 = open (name1, O_RDWR ))<0)
    	{
         perror("Could not open named pipe.");
    	}
	if ((fd2 = open (name2, O_RDWR ))<0)
    	{
         perror("Could not open named pipe.");
    	}

這是檢視當前目錄,可以看到兩個Pipe檔案,它和普通檔案不同,具有p屬性,表明是一個管道檔案。

2)Java中,使用檔案讀寫的方式開啟這兩個檔案,即可進行讀寫。

			String namedPipe1=workdir+"/process_in";
			String namedPipe2=workdir+"/process_out";

			File pipe1 = new File(namedPipe1);
			File pipe2 = new File(namedPipe2);
			
			BufferedReader reader = new BufferedReader(new FileReader(pipe2));
			//DataInputStream reader = new DataInputStream(new FileInputStream(pipe2));
			BufferedWriter writer = new BufferedWriter(new FileWriter(pipe1));

			writer.write("test");

            char [] buf = new char[8];
			reader.read(buf);


 3)需要注意的問題

  1)Java最好用單獨的執行緒來讀資料;

  2)Java寫完資料後,需要flush();