• 用C代码简要模拟实现一下RPC(远程过程调用)并谈谈它在代码调测中的重要应用【转】


    转自:http://blog.csdn.net/stpeace/article/details/44947925

            明: 本文仅仅是一种模拟的RPC实现, 真正的RPC实现还是稍微有点复杂的。


            我们来看看下面这个常见的场景: 在某系统中,我们要对某一函数进行调测, 但是, 很难很难构造出这个函数被调用的实际场景, 怎么办?

            虽然很难构造出这个函数被调用的实际场景, 但我们完全可以在代码中主动调用这个函数啊。多想方法(直接方法和间接方法), 少找借口, 并且坚信方法总是存在的。我们可以搞一个触发的操作, 每触发一次, 就调用到该系统中的该函数。 可是, 如果这个系统比较封闭, 比如是某嵌入式系统, 也不好触发。 没关系, 我们借用RPC的思路来实现: 让这个系统做服务端, 然后在客户端上触发。


            什么是RPC(远程过程调用)呢?度娘介绍了很多, 我不想搞那么复杂, 所以用一句白话来解释RPC: 进程A向进程B发送消息, 触发进程B的函数被执行,这样, 从形式上看, 好像就是进程A远程调用了进程B的函数, 这就是所谓的RPC(实际上, 进程A仅仅是触发而已, 真正执行的仍然是进程B, 但理解为进程A远程调用了进程B的函数, 也是很爽的)

            下面, 基于上面介绍的代码调测场景, 我来简要实现一下RPC:


            服务端程序为(进程B):

    1. #include <stdio.h>  
    2. #include <winsock2.h> // winsock接口  
    3. #pragma comment(lib, "ws2_32.lib") // winsock实现  
    4.   
    5. SOCKET sockConn; // 全局的通信socket  
    6.   
    7.   
    8. // RPC函数(Remote Procedure Calling)  
    9. void readIP()  
    10. {  
    11.     printf("ip is 192.168.1.100 ");  
    12. }  
    13.   
    14. // RPC函数(Remote Procedure Calling)  
    15. void readMask()  
    16. {  
    17.     printf("mask is 255.255.255.0 ");    
    18. }  
    19.   
    20. // RPC函数(Remote Procedure Calling)  
    21. void readGateway()  
    22. {  
    23.     printf("gateway is 192.168.1.1 ");   
    24. }  
    25.   
    26. // 消息处理线程  
    27. DWORD WINAPI handleThread(LPVOID pM)    
    28. {  
    29.     while(1)  
    30.     {  
    31.         char szMsg[100] = {0};  
    32.         int nRet = recv(sockConn, szMsg, sizeof(szMsg) - 1, 0);  
    33.         if(nRet <= 0)  
    34.         {  
    35.             printf("recv error ");  
    36.             closesocket(sockConn);  
    37.             break;  
    38.         }  
    39.   
    40.         // 仅仅考虑读操作, 预期的形式为: read xxx  
    41.         char szOperType[20] = {0};  
    42.         char szParaName[50] = {0};  
    43.         nRet = sscanf(szMsg, "%s %s", szOperType, szParaName);  
    44.         if(2 != nRet)  
    45.         {  
    46.             printf("command error ");  
    47.             continue;  
    48.         }  
    49.   
    50.         if(0 != strcmp(szOperType, "read"))  
    51.         {  
    52.             printf("type error ");  
    53.             continue;  
    54.         }  
    55.   
    56.         // 其实, 下面的部分最好用C++ STL的map来做, 为了简便示意, 我就没用map搞了  
    57.         if(0 == strcmp(szParaName, "ip"))  
    58.         {  
    59.             readIP();  
    60.         }  
    61.         else if(0 == strcmp(szParaName, "mask"))  
    62.         {  
    63.             readMask();  
    64.         }  
    65.         else if(0 == strcmp(szParaName, "gateway"))  
    66.         {  
    67.             readGateway();  
    68.         }  
    69.         else  
    70.         {  
    71.             printf("parameter error ");  
    72.             continue;  
    73.         }  
    74.   
    75.         Sleep(200);  
    76.     }  
    77.   
    78.     return 0;  
    79. }   
    80.   
    81. int main()  
    82. {  
    83.     WORD wVersionRequested;  // 双字节,winsock库的版本  
    84.     WSADATA wsaData;         // winsock库版本的相关信息  
    85.       
    86.     wVersionRequested = MAKEWORD(1, 1); // 0x0101 即:257  
    87.       
    88.   
    89.     // 加载winsock库并确定winsock版本,系统会把数据填入wsaData中  
    90.     WSAStartup( wVersionRequested, &wsaData );  
    91.       
    92.   
    93.     // AF_INET 表示采用TCP/IP协议族  
    94.     // SOCK_STREAM 表示采用TCP协议  
    95.     // 0是通常的默认情况  
    96.     unsigned int sockSrv = socket(AF_INET, SOCK_STREAM, 0);  
    97.   
    98.     SOCKADDR_IN addrSrv;  
    99.   
    100.     addrSrv.sin_family = AF_INET; // TCP/IP协议族  
    101.     addrSrv.sin_addr.S_un.S_addr = inet_addr("0.0.0.0"); // socket对应的IP地址  
    102.     addrSrv.sin_port = htons(8888); // socket对应的端口  
    103.   
    104.     // 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程)  
    105.     bind(sockSrv,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));  
    106.   
    107.     // 将socket设置为监听模式,5表示等待连接队列的最大长度  
    108.     listen(sockSrv, 5);  
    109.   
    110.   
    111.     // sockSrv为监听状态下的socket  
    112.     // &addrClient是缓冲区地址,保存了客户端的IP和端口等信息  
    113.     // len是包含地址信息的长度  
    114.     // 如果客户端没有启动,那么程序一直停留在该函数处  
    115.     SOCKADDR_IN addrClient;  
    116.     int len = sizeof(SOCKADDR);  
    117.     sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);  
    118.   
    119.     // 开启消息处理线程  
    120.     HANDLE handle = CreateThread(NULL, 0, handleThread, NULL, 0, NULL);     
    121.   
    122.     while(1); // 卡住, 表示主线程去做自己的事情, 忙自己的东西  
    123.   
    124.     CloseHandle(handle);  
    125.     closesocket(sockConn);    
    126.     closesocket(sockSrv);  
    127.     WSACleanup();  
    128.       
    129.     return 0;  
    130. }  

           启动服务端。


           然后看看客户端(进程A):

    1. #include <winsock2.h>  
    2. #include <stdio.h>  
    3. #pragma comment(lib, "ws2_32.lib")  
    4.   
    5. int main()  
    6. {  
    7.     WORD wVersionRequested;  
    8.     WSADATA wsaData;  
    9.     wVersionRequested = MAKEWORD(1, 1);  
    10.   
    11.     SOCKET sockClient = 0;  
    12.   
    13.     WSAStartup( wVersionRequested, &wsaData );  
    14.     sockClient = socket(AF_INET, SOCK_STREAM, 0);  
    15.     SOCKADDR_IN addrSrv;  
    16.     addrSrv.sin_addr.S_un.S_addr = inet_addr("192.168.1.100"); // 请替换为合适的ip  
    17.     addrSrv.sin_family = AF_INET;  
    18.     addrSrv.sin_port = htons(8888);  
    19.     connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));  
    20.   
    21.     while(1)  
    22.     {  
    23.         char szOpenType[20] = {0};  
    24.         char szParaName[50] = {0};  
    25.   
    26.         // 客户端发消息给服务端, 触发服务端RPC函数执行, 这样, 就感觉是客户端进程在调用服务器进程里面的函数, 爽歪歪啊!  
    27.         scanf("%s", szOpenType);  
    28.         scanf("%s", szParaName);  
    29.   
    30.         char szMsg[100] = {0};  
    31.         sprintf(szMsg, "%s %s", szOpenType, szParaName); // 其实, sprintf不太安全哈  
    32.         send(sockClient, szMsg, strlen(szMsg) + 1, 0);  
    33.     }  
    34.   
    35.     closesocket(sockClient);  
    36.     WSACleanup();  
    37.   
    38.     return 0;  
    39. }  

          好, 开启客户端。


          下面是执行结果:



           我们看到, 客户端远程调用到了服务端的函数, 这就是所谓的RPC.  在实际代码调测中, 我们经常需要主动触发某一函数或某一部分代码的执行, 一般来说, 怎么方便怎么触发, 本文介绍的RPC触发方式是值得考虑的一种方法。 通过本文的学习, 我们也算初步了解了RPC吧。

  • 相关阅读:
    Java深度历险(四)——Java垃圾回收机制与引用类型
    Java深度历险(三)——Java线程​:基本概念、可见性与同步
    Java深度历险(一)——Java字节代码的操纵(转)
    SVN问题:Cleanup failed to process the following paths: xxxxxx
    解决 ORA-12154: TNS:could not resolve service name
    Linq 实现普通sql中 where in 的功能
    C# 操作电脑 关机 重启 注销 休止 休眠
    使用C#代码追加和提交文件到SVN服务器
    access 2007 vba (亖)
    access 2007 vba 开发中学到的知识(三)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/8529289.html
Copyright © 2020-2023  润新知