[102] JUCE Diary #12 : 答客問:InterprocessConnectionServer
網友在這篇文章問到 InterprocessConnectionServer 與 InterprocessConnection 的使用方式。本文以一簡易程式來說明如何使用這兩個類別。範例程式做的事情如下:
- 啟動一個 Server 程式監聽網路 Port 52713
- 啟動一個 Client 程式連接到本機(localhost) Port 52713
- 連線成功後,Client 會傳送資料到 Server
- Server 收到資料進行處理
Client 與 Server 皆為 JUCE Console Project,文章內的程式碼皆為片段,完整的程式碼請見文末的 Gist。由於只是示範,故省略許多錯誤判斷,請多留意。
Server-side: InterprocessConnectionServer
先來看 InterprocessConnectionServer 的介面:
class InterprocessConnectionServer : private Thread { // ①
public:
bool beginWaitingForSocket (int portNumber);
void stop();
protected:
virtual InterprocessConnection* createConnectionObject() = 0; // ②
private:
ScopedPointer<StreamingSocket> socket;
void run() override;
};
① 以 private
繼承自 Thread 類別,使得 Thread 原本公開的成員函式變得無法被外界存取。此舉將 InterprocessConnectionServer 對外(public)介面變成只有兩個:
- beginWaitingForSocket
- stop
② InterprocessConnectionServer 最重要的是名為 createConnectionObject
的「Pure Virtual Function(純虛擬函式)」,因為她,所以無法直接將 InterprocessConnectionServer 實體化(具現化?),用者必須繼承它並實作 createConnectionObject
成員函式才可以使用 InterprocessConnectionServer 提供的服務。底下的 IPCServer 為示範:
class IPCServer : public InterprocessConnectionServer
{
public:
IPCServer()
: connection_(nullptr) {}
~IPCServer()
{
delete connection_;
}
protected:
InterprocessConnection* createConnectionObject() override
{
connection_ = new Connection();
return connection_;
}
Connection* connection_;
};
createConnectionObject
只是簡單建立一個 Connection 物件,將其存於成員變數(於解構式中將其釋放),然後回傳。可是,Connection 類別哪來的?繼續看下去。
Server-side: InterprocessConnection
InterprocessConnection 類別也有**「純虛擬函式」**,所以也不能直接具現化。先看看她的介面(刪去不重要的部分):
class InterprocessConnection {
public:
InterprocessConnection (bool callbacksOnMessageThread = true,
uint32 magicMessageHeaderNumber = 0xf2b49e2c);
bool connectToSocket (const String& hostName,
int portNumber,
int timeOutMillisecs);
virtual void connectionMade() = 0;
virtual void connectionLost() = 0;
virtual void messageReceived (const MemoryBlock& message) = 0;
};
要覆寫的「純虛擬函式」有三個: connectionMade
, connectionLost
, messageReceived
。這三個函式皆為 CALLBACK 函式,會在特定時機被呼叫,由名稱可猜出她們被呼叫的時機點:
- connectionMade:連線成功時呼叫
- connectionLost:斷線時呼叫
- messageReceived:接收到資料時呼叫
底下是我的 Connection 類別實作:
class Connection : public InterprocessConnection {
public:
Connection()
: InterprocessConnection(false, 15) {} // ①
void connectionMade() override { printf("Connection made\n"); }
void connectionLost() override { printf("Connection lost\n"); }
void messageReceived(const MemoryBlock& msg) override { // ②
printf("From client: %s\n", msg.toString().toRawUTF8());
}
};
- ① Connection 的建構式(ctor)呼叫 InterprocessConnection 建構式並傳入相應的參數值。
- ②
messageReceived
假設傳來的資料為字串,收到後直接將其印出。
Client-side: InterprocessConnection
在客戶端(Client-side)這邊同樣需要實作 InterprocessConnection ,作法同 Server-side InterprocessConnection,故不在此重覆。(完整範例見文末連結)
指揮艇,組合!
萬事俱備,可以開始實作功能了。
① 啟動一個 Server Process 監聽網路 Port 52713
先在 Server-side 建立一個 IPCServer 物件,並呼叫 beginWaitingForSocket
成員函式:
IPCServer server;
if (server.beginWaitingForSocket(52713)) {
printf("Waiting for client...\n");
}
server.beginWaitingForSocket
若成功表示已經開始監聽 Port 52713。
② 啟動一個 Client Process 連接到本機(localhost)Port 52713
在 Client-side 建立 Connection 物件,並呼叫其 connectToSocket
成員函式:
std::unique_ptr<Connection> client (new Connection());
client->connectionToSocket("localhost", 52713, 5000);
呼叫 connectionToSocket
會觸發 Server-side IPCServer 使其呼叫 createConnectionObject
用以建立 InterprocessConnection 物件,成功建立後會呼叫其 connectionMade
。
③ Client 傳送資料給 Server
Client 在確認連線成功後利用 sendMessage
傳送字串,送完後自動斷線(disconnect
)。
if (client->isConnected()) {
printf("Connected\n");
MemoryBlock mb;
String msg("713");
mb.append(msg.toRawUTF8(), msg.length());
client->sendMessage(mb);
client->disconnect();
}
④ Server 收到 Client 傳來的並資料進行處理
當 Client-side 呼叫 sendMessage
,Server-side IPCServer 裡的 messageReceived
會被呼叫:
void messageReceived(const MemoryBlock& msg) override {
const auto str = msg.toString();
printf("From client: %s\n", str.toRawUTF8());
if (str.contains("713")) {
// stop server
}
}
JUCE 使用 MemoryBlock 來包裝傳送與接收的資料,MemoryBlock 提供許多成員函式,其中 toString
將資料轉成字串,很方便。
底下展示 Server(左邊)與 Client(右邊)的執行實況:
執行 server 程式後出現的警告訊窗為 OS X 的安全機制。
完整範例
- Server: https://gist.github.com/mht/e07604190015d0f909aa17cea5db864b
- Client: https://gist.github.com/mht/f98fd3deeffed226fb4e8b2479e87834
其實 JUCE 隨附的 JuceDemo 專案(examples/Demo)有大量的範例可參考,不過 IPC 這主題 JuceDemo 示範的是 ChildProcessMaster/ChildProcessSlave 類別,剛好沒有 InterprocessConnectionServer
。
接下來
本文以程式碼示範例 InterprocessConnectionServer 的使用方式,下一篇打算探討其設計應用了哪些 SOLID 原則。🔚