1. 程式人生 > >用C程式碼簡要模擬實現一下RPC(遠端過程呼叫)並談談它在程式碼調測中的重要應用

用C程式碼簡要模擬實現一下RPC(遠端過程呼叫)並談談它在程式碼調測中的重要應用

        明: 本文僅僅是一種模擬的RPC實現, 真正的RPC實現還是稍微有點複雜的。

        我們來看看下面這個常見的場景: 在某系統中,我們要對某一函式進行調測, 但是, 很難很難構造出這個函式被呼叫的實際場景, 怎麼辦?

        雖然很難構造出這個函式被呼叫的實際場景, 但我們完全可以在程式碼中主動呼叫這個函式啊。多想方法(直接方法和間接方法), 少找藉口, 並且堅信方法總是存在的。我們可以搞一個觸發的操作, 每觸發一次, 就呼叫到該系統中的該函式。 可是, 如果這個系統比較封閉, 比如是某嵌入式系統, 也不好觸發。 沒關係, 我們借用RPC的思路來實現: 讓這個系統做服務端, 然後在客戶端上觸發。

        什麼是RPC(遠端過程呼叫)呢?度娘介紹了很多, 我不想搞那麼複雜, 所以用一句白話來解釋RPC: 程序A向程序B傳送訊息, 觸發程序B的函式被執行,這樣, 從形式上看, 好像就是程序A遠端呼叫了程序B的函式, 這就是所謂的RPC(實際上, 程序A僅僅是觸發而已, 真正執行的仍然是程序B, 但理解為程序A遠端呼叫了程序B的函式, 也是很爽的)

        下面, 基於上面介紹的程式碼調測場景, 我來簡要實現一下RPC:

        服務端程式為(程序B):

#include <stdio.h>
#include <winsock2.h> // winsock介面
#pragma comment(lib, "ws2_32.lib") // winsock實現

SOCKET sockConn; // 全域性的通訊socket


// RPC函式(Remote Procedure Calling)
void readIP()
{
	printf("ip is 192.168.1.100\n");
}

// RPC函式(Remote Procedure Calling)
void readMask()
{
	printf("mask is 255.255.255.0\n");	
}

// RPC函式(Remote Procedure Calling)
void readGateway()
{
	printf("gateway is 192.168.1.1\n");	
}

// 訊息處理執行緒
DWORD WINAPI handleThread(LPVOID pM)  
{
	while(1)
	{
		char szMsg[100] = {0};
		int nRet = recv(sockConn, szMsg, sizeof(szMsg) - 1, 0);
		if(nRet <= 0)
		{
			printf("recv error\n");
			closesocket(sockConn);
			break;
		}

		// 僅僅考慮讀操作, 預期的形式為: read xxx
		char szOperType[20] = {0};
		char szParaName[50] = {0};
		nRet = sscanf(szMsg, "%s %s", szOperType, szParaName);
		if(2 != nRet)
		{
			printf("command error\n");
			continue;
		}

		if(0 != strcmp(szOperType, "read"))
		{
			printf("type error\n");
			continue;
		}

		// 其實, 下面的部分最好用C++ STL的map來做, 為了簡便示意, 我就沒用map搞了
		if(0 == strcmp(szParaName, "ip"))
		{
			readIP();
		}
		else if(0 == strcmp(szParaName, "mask"))
		{
			readMask();
		}
		else if(0 == strcmp(szParaName, "gateway"))
		{
			readGateway();
		}
		else
		{
			printf("parameter error\n");
			continue;
		}

		Sleep(200);
	}

	return 0;
} 

int main()
{
	WORD wVersionRequested;  // 雙位元組,winsock庫的版本
	WSADATA wsaData;         // winsock庫版本的相關資訊
	
	wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257
	

	// 載入winsock庫並確定winsock版本,系統會把資料填入wsaData中
	WSAStartup( wVersionRequested, &wsaData );
	

	// AF_INET 表示採用TCP/IP協議族
	// SOCK_STREAM 表示採用TCP協議
	// 0是通常的預設情況
	unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);

	SOCKADDR_IN addrSrv;

	addrSrv.sin_family = AF_INET; // TCP/IP協議族
	addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket對應的IP地址
	addrSrv.sin_port = htons(8888); // socket對應的埠

	// 將socket繫結到某個IP和埠(IP標識主機,埠標識通訊程序)
	bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	// 將socket設定為監聽模式,5表示等待連線佇列的最大長度
	listen(sockSrv, 5);


	// sockSrv為監聽狀態下的socket
	// &addrClient是緩衝區地址,儲存了客戶端的IP和埠等資訊
	// len是包含地址資訊的長度
	// 如果客戶端沒有啟動,那麼程式一直停留在該函式處
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);
	sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);

	// 開啟訊息處理執行緒
	HANDLE handle = CreateThread(NULL, 0, handleThread, NULL, 0, NULL);   

	while(1); // 卡住, 表示主執行緒去做自己的事情, 忙自己的東西

	CloseHandle(handle);
	closesocket(sockConn);	
	closesocket(sockSrv);
	WSACleanup();
	
	return 0;
}
       啟動服務端。

       然後看看客戶端(程序A):

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")

int main()
{
	WORD wVersionRequested;
	WSADATA wsaData;
	wVersionRequested = MAKEWORD(1, 1);

	SOCKET sockClient = 0;

	WSAStartup( wVersionRequested, &wsaData );
	sockClient = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.100"); // 請替換為合適的ip
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(8888);
	connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));

	while(1)
	{
		char szOpenType[20] = {0};
		char szParaName[50] = {0};

		// 客戶端發訊息給服務端, 觸發服務端RPC函式執行, 這樣, 就感覺是客戶端程序在呼叫伺服器程序裡面的函式, 爽歪歪啊!
		scanf("%s", szOpenType);
		scanf("%s", szParaName);

		char szMsg[100] = {0};
		sprintf(szMsg, "%s %s", szOpenType, szParaName); // 其實, sprintf不太安全哈
		send(sockClient, szMsg, strlen(szMsg) + 1, 0);
	}

	closesocket(sockClient);
	WSACleanup();

	return 0;
}
      好, 開啟客戶端。

      下面是執行結果:



       我們看到, 客戶端遠端呼叫到了服務端的函式, 這就是所謂的RPC.  在實際程式碼調測中, 我們經常需要主動觸發某一函式或某一部分程式碼的執行, 一般來說, 怎麼方便怎麼觸發, 本文介紹的RPC觸發方式是值得考慮的一種方法。 通過本文的學習, 我們也算初步瞭解了RPC吧。