1、開篇 程序間通訊的主要目的是實現多臺計算機(也能夠是同一臺)中應用程序之間的數據共享與信息交換。在不一樣的計算機系統中,它們之間要經過網絡之間的協議才能實現數據共享與信息交換;在同一臺計算機系統中,它們之間只需必定的通道就能實現數據共享與信息交換。在不一樣計算機系統和同一計算機系統的程序通訊中,既有不少相同之處,也有各自的特色。程序間通訊都要靠必定的通道(pipe)來實現,其中的通道多種多樣,各俱特點。 爲了充分認識和掌握程序間通訊及其相應的實現技術,本文對各類通訊方法進行討論。包括每種方法的原理和實現等。 2、各類通訊途徑及實現 首先,程序間相互獨立,它的運行環境不爲別的進程所改變。 I.[socket] SOCKET編程是一種典型的會話編程方式,相似於老師家訪,敲門----有人開門----進去----交流----出門。它適用於client/server通訊方式,也適用於點對點通訊方式。 下面分別介紹服務端和客戶端的具體任務。 這裏介紹TCP的過程。 (1)服務端 服務端首先建立一個套接字,使用socket()調用;而後使用bind()將該套接字與本地IP和某一端口相關聯(該端口能夠是空閒的也能夠是非空閒的,具體的能夠參閱筆者的《對端口截聽的實現》這篇文章)。使用listen()讓套接字等候進入鏈接,而後用accept()使套接字做好接受客戶鏈接的準備。當鏈接請求到來後,被阻塞服務進程的accept()函數生成一個新的套接字與客戶套接字創建鏈接,並向客戶返回接收信號。用read()來讀入數據,用write()來向發送進程寫回一些數據,如確認信息或回顯信息。 (2)客戶端 客戶進程也是先建立一個套接字,用socket()調用,而後客戶向服務進程發出鏈接請求,經過調用connect()能夠創建一個端的鏈接,鏈接成功後用write()向服務端發送數據,用read()讀取服務端返回的數據。 這種方式隱含了在創建client/server間通訊的非對稱性。client/server模型工做時要求有一套爲客戶機和服務器所共識的慣例來保證服務器可以被提供(或被接受),這一套慣例包括了一套協議,它必須在通訊的兩頭都被實現。根據不一樣的實際狀況,協議多是對稱的或是非對稱的。在對稱的協議中,每一方都有可能扮演主從角色;在非對稱協議中,每一放則是從機。例如終端仿真TELNET是對稱協議,HTTP是非對稱協議。不管具體的協議是對稱的仍是非對稱的,當服務被提供時必然存在客戶進程和服務進程。 這種通訊方式適用於單個計算機系統,也適用於多個計算機系統。 ● WSADATA wsaData; WORD version = MAKEWORD(2, 0); int ret = WSAStartup(version, &wsaData); if(ret != 0) TRACE("Initilize Error!\n"); m_hSocket = socket(AF_INET, SOCK_STREAM,0); m_addr.sin_family = AF_INET; m_addr.sin_addr.S_un.S_addr = INADDR_ANY; m_addr.sin_port = htons(m_nPort); int ret = 0; int error = 0; ret = bind(m_hSocket, (LPSOCKADDR)&m_addr, sizeof(m_addr)); if(ret == SOCKET_ERROR){ TRACE("Bind Error: %d \n", (error = WSAGetLastError())); return ; } ret = listen(m_hSocket, 2); if(ret == SOCKET_ERROR){ TRACE("Listen Error: %d \n", (error = WSAGetLastError())); return ; } SOCKET s = accept(m_hSocket, NULL, NULL); if(s == SOCKET_ERROR){ TRACE("Accept Error: %d \n", (error = WSAGetLastError())); return ; } char buff[256]; ret = recv(s, buff, 256, 0); if(ret == 0 || ret == SOCKET_ERROR ){ TRACE("Recv data error: %d\n", WSAGetLastError()); return ; } char buf[]="hello"; send(s, buf, str.GetLength(), 0); II.[file] 這種方法很原始,使用率也不多,並且佔用磁盤空間。利用文件做爲程序間通訊的通道,程序A向一文件寫入數據,程序B經過讀取這個文件中的數據從而與程序A進行通訊,同時這一過程也能夠是反向的。這種方法使用的前提是運行程序的用戶須要具備對磁盤讀/寫的權限。 |A.WriteFile------>fileA<------B.ReadFile(check every <time>) |A.ReadFile(check every <time>)----->fileB<-----B.WriteFile ● //A int buff=0; ofstream Table; Table.open("c:\\temp.txt"); Table<<buff<<endl; Table.close(); //B int buff=1; for(;;) { ifstream Table; Table.open("c:\\temp.txt"); Table>>buff; Table.close(); if(buff==0){ printf("Operation code equal 0"); //... return; } sleep(10); } 這種方法適用於單個計算機系統也適用於多個計算機系統。用這種方法在多個計算機系統間進行控制和數據交換,能夠將這個文件放與一計算機上,對方創建空鏈接,COPY此文件到本地,進行讀取。固然也還有別的不少方法。 III.[signal] 這種方法僅用於進程控制,不能進行數據交換。例如在發生非法內存訪問、執行無效指令、某些按鍵(如CTRL+C,DEL等)等都會產生一個信號,其餘程序調用有關的系統調用或用戶自定義的處理過程進行處理。 信息處理的系統調用是signal,它的原型是void ( *signal( int sig, void (__cdecl *func) ( int sig [, int subcode ] )) ) ( int sig );這個函數可讓一個進程選擇不少方法中的一種去處理操做系統返回的中斷訊號。 根據相似的思路,咱們也能夠在多個程序間利用「信號」進行程序間的通訊。 這種方法的關鍵是產生信號。能夠經過keybd_evnet()產生信號,這個函數可以起到模擬鍵盤輸出的做用,它的函數原型是VOID keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,DWORD dwExtraInfo);vVk是一個虛擬鍵碼,bScan是掃描碼,對應相應的鍵。 這種方法適用於單個計算機系統。 IV.[pipe] 管道(PIPE)是一種簡單的進程間通訊(IPC)機制,分爲無名管道和有名管道兩種。命名管道能夠在同一臺機器的不一樣進程間以及不一樣機器上的不一樣進程間進行雙向通訊(使用UNC命名規範)。管道的最大好處在於:它能夠象對普通文件同樣進行操做,它的操做表示符HANDLE,也就是說,它可使用ReadFile,WriteFile函數進行與底層實現無關的讀寫操做,用戶根本就沒必要了解網絡間/進程間通訊的具體細節。 無名管道其實是內存中的一個臨時存儲區,由系統安全控制,而且獨立於建立它的進程的內存區。管道對數據採用先進先出的方式管理,並嚴格按順序操做,管道不能被搜索,管道中的信息只能讀一次。無名管道只是在父子進程之間或者一個進程的兩個子進程之間進行通訊的。它是單向的。無名管道實際上是經過用給了一個指定名字的有名管道來實現的。 有名管道的操做和無名管道相似。 管道的建立用createpipe()函數,用readfile()、writefile()對其進行操做。 ● SECURITY_ATTRIBUTES a; HANDLE hReadPipe,hWritePipe; CreatePipe(&hReadPipe,&hWritePipe,&a,0); unsigned long lBytesRead; char Buff[1024]; int ret; while(1) { ret=PeekNamedPipe(hReadPipe,Buff,1024,&lBytesRead,0,0); if(lBytesRead) { ret=ReadFile(hReadPipe,Buff,lBytesRead,&lBytesRead,0); if(!ret) break; } else { //lBytesRead=...; if(lBytesRead<=0) break; ret=WriteFile(hWritePipe,Buff,lBytesRead,&lBytesRead,0); if(!ret) break; } } V.[MQ] 消息通訊方式以消息緩衝區爲中間介質,通訊雙方的發送和接收操做均以消息爲單位。在存儲器中,消息緩衝區被組織成隊列,一般稱爲消息隊列(MQ)。消息隊列是獨立於生成它的進程的一段存儲區,任何具備正確訪問權限的進程均可以訪問消息隊列,它很是適用於在進程間交換短消息。 其中,每條消息有類型編號分類。消息隊列在建立後將一直存在,直到使用相關的調用刪除它爲止。 VI.[共享儲存段(SM)] 共享儲存段通訊(shared memory)容許多個進程在外部通訊協議或同步/互斥機制的支持下使用同一個內存段做爲中間介質進行通訊,它是一種頗有效的數據通訊方式。 在進行通訊以前,先建立一個共享內存段,而後進行映射和分離操做。同時能夠根據須要改變共享內存段的存取權限以及其餘的一些特徵。 ☆對於下面的集中通訊方式,就要提到「中間件(Middleware)」。 中間件是一類軟件,它對應用程序隱含了實際網絡和通訊協議的細節。高級編程接口幫助開發者在不一樣的環境中建立應用程序,而不須要對將使用的網絡和通訊協議有更多的瞭解。 正常狀況下,中間件在使用不一樣網絡通訊協議的客戶機/服務器環境。它能夠對客戶機/服務器應用隱藏協議,從而使開發人員集中精力於改進應用程序,而不是開發通訊接口。 中間件產品隱藏了前端應用程序和後端應用程序的區別。中間件層包括通用應用程序和流行應用程序的應用程序編程接口(API)之間的翻譯功能。例如,Microsoft的開放式數據庫鏈接(ODBC)標準提供後端數據庫系統操做的通用功能。前端應用程序寫入ODBC,並利用它的功能。ODBC 隱藏了不一樣廠商的SQL實現的區別。Microsoft以一組Microsoft Windows驅動程序的形式提供ODBC,以提供對Microsoft Access、Microsoft Excel、Microsoft SQL Server、FoxPro、Btrieve,dBASE,Borland Paradox、IBM DB二、DECRdb和Oracle等格式產生的數據的訪問。ODBC是爲了使Windows成爲客戶訪問後端數據庫的標準而設計的。 □用戶須要訪問許多不一樣的後端服務器上的服務。 □後端服務器可使用不一樣的操做系統,並須要不一樣的通訊協議。 □後端數據庫服務器有不兼容的SQL命令集,它使用戶和程序員很難從一個系統轉到另外一個系統。 □限制用戶只能使用某種特定的訪問後端服務的應用程序如今已經不現實。用戶須要從不一樣的應用程序來訪問服務。 □新的模型是爲了使用戶在多廠商環境使用多種協議在任何前端來訪問任何後端服務。 在多協議、多廠商環境,一般一個程序員須要編寫應用程序,來與每一個協議和支持系統進行工做。使用中間件,程序員只需簡單地編寫到中間件的接口,而由中間件處理全部多協議、多廠商問題。有三種類型的中間件:遠程過程調用(RPC)、會晤(conversations)和消息傳遞系統。它們都能很好地隱藏通訊過程,以及與它們進行操做的系統差別。 VII.[RPC] Remote Procedure Call(PRC) 一個遠程過程調用是在網絡上一個計算機系統對另外一計算機系統的請求。RPC保持了中間件在不一樣網絡平臺和通訊協議上工做的性質。基本上,一個PRC就是一臺計算機向另外一臺計算機發出的直接請求。它是一種請求/回答的過程,這時請求放等待一個回答,這意味着PRC一般是在面向鏈接界面上發生的一種實時呼叫。 RPC機制強制通訊的兩個應用程序必須同時處於運行狀態。作遠程調用時,二者必須先創建鏈接,並且通信鏈路質量對它的效果影響很大。 它的工做方式是:當一個應用程序A須要與遠程的另外一個應用程序B交換信息或要求B提供協助時,A將在本地產生一個請求,經過通信鏈路,同志B接收信息或提供相應的服務,B完成相關處理後將確認信息或結果返回給A。 PRC的優勢是應用程序採用調用/返回方式通信,擁有很高的潛在效率,但須要應用程序間的緊密耦合,通信線路必須在通訊期間一直保持良好的狀態,並且必須進行大量的底層通信的編程工做。 VIII.[conversations] 會晤(conversations)是邏輯鏈接的兩個或多個系統之間的連續會話,同RPC不一樣,在分佈式環境,會晤可能會重疊執行。出於這個緣由,對須要在多個地方必須徹底同步地完成,修改分佈式數據庫的工做,會晤是很是必要和有益的。IBM的高級程序對程序通訊(APPC)實現了會晤。用於實現會晤的OSI標準也已出現。Covia Technologies(Rosemont,Illinois)的通訊集成器(CI,Communication Integrator)是具備會晤的中間件的另外一個例子、它能夠在大型計算機、中型機和臺式機上運行。 IX.[MQSeries消息隊列] 爲了簡化應用程序間的通訊,使得通訊具備較高的可靠性,又保證明現的簡單性,中間件技術是咱們的首選,IBM公司的MQSeries就是基於這種技術的產品。MQSeries是一種獨立的通訊軟件,應用程序只需將任務提交給該軟件,就能夠由該軟件自動去完成信息的傳遞工做。 例如: 應用程序A | | 放入|消息 | | ∨ MQSeries接口---------------- ∧ | | | | |喚醒 讀取|消息(於Q1中) | | | | | 應用程序B(不管本地仍是遠程)<----- 應用程序間的消息傳遞是經過隊列來實現的,是間接的,因此即便B沒能正常運行時,A仍然能正常運行,並且,消息還能夠喚醒B。 可見,MQSeries的優勢是能夠確保信息是永久的、可恢復的,確保信息傳遞的安全性,確保信息的成功發送且只發送一次,同時,還可使各應用程序能夠獨立的正常運行。 不管是共有的仍是私有的隊列,都是用MQOpenQueue()打開,MQOpenQueue函數用來打開一個用來發送消息或是讀取消息的隊列。它的原型是HRESULT APIENTRY MQOpenQueue( LPCWSTR lpwcsFormatName, DWORD dwAccess, DWORD dwShareMode, LPQUEUEHANDLE phQueue );其中,lpwcsFormatName是用戶指定的指向隊列的format name字符串,這個format name能夠是public,private,或者是direct format。dwAccess是由用戶指定的應用程序使用隊列的方法,能夠是peek,send,receive。當隊列打開之後這個設置就不能被更改。dwAccess有三種模式:MQ_PEEK_ACCESS/MQ_SEND_ACCESS/MQ_RECEIVE_ACCESS。dwShareMode是由用戶指定的隊列的共享模式,分爲:MQ_DENY_NONE和MQ_DENY_RECEIVE_SHARE,MQ_DENY_NONE是默認值,容許任何人使用,若是dwAccess設置成了MQ_SEND_ACCESS,那麼在此就必須設置成MQ_DENY_NONE;MQ_DENY_RECEIVE_SHARE限制了今後隊列讀取消息的進程。若是這個隊列已經被別的進程打開用來接收消息,那麼將返回MQ_ERROR_SHARING_VIOLATION,MQ_DENY_RECEIVE_SHARE僅適用於當dwAccess設置成MQ_RECEIVE_ACCESS或MQ_PEEK_ACCESS的時候。phQueue是輸出的已打開的隊列的句柄。 而後能夠用MQSendMessage()/MQReceiveMessage()等函數進行具體操做,在此很少熬述。 3、結束語 技術是一點一滴積累起來的,對任何知識點的系統性掌握頗有必要也頗有用。 各類通訊的實現技術都具備本身的特色和使用範圍,管道、消息隊列、共享內存等技術最適用於同一計算機系統內部的進程間通訊,以保證高效率。而遠程過程調用、Socket會話編程、MQSeries則最適用於遠程的應用程序之間通訊,能夠簡化通訊的編程,固然也保證通訊的可靠性。尤爲是MQSeries,它是一個比較完善的中間件產品,爲許多的信息系統所選用。 對各類通訊技術的合理搭配使用,經常能起到事半功倍的做用。