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

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

启动服务端。

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

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

好, 开启客户端。

下面是执行结果:

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

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

  1. .net单元测试——常用测试方式(异常模拟、返回值测试、参数测试、数据库访问代码测试)

    最近在看.net单元测试艺术,我也喜欢单元测试,今天介绍一下如何测试异常.如何测试返回值.如何测试模拟对象的参数传递.如何测试数据库访问代码.单元测试框架使用的是NUnit,模拟框架使用的是:Rhin ...

  2. 利用Python中的mock库对Python代码进行模拟测试

    这篇文章主要介绍了利用Python中的mock库对Python代码进行模拟测试,mock库自从Python3.3依赖成为了Python的内置库,本文也等于介绍了该库的用法,需要的朋友可以参考下     ...

  3. tolua#代码简要分析

    简介 tolua#是Unity静态绑定lua的一个解决方案,它通过C#提供的反射信息分析代码并生成包装的类.它是一个用来简化在C#中集成lua的插件,可以自动生成用于在lua中访问Unity的绑定代码 ...

  4. 编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则)

    编写高质量代码:改善Java程序的151个建议(第一章:JAVA开发中通用的方法和准则) 目录 建议1: 不要在常量和变量中出现易混淆的字母 建议2: 莫让常量蜕变成变量 建议3: 三元操作符的类型务 ...

  5. CODING 代码资产安全系列之 —— 构建全链路安全能力,守护代码资产安全

    本文作者:王振威 - CODING 研发总监 CODING 创始团队成员之一,多年系统软件开发经验,擅长 Linux,Golang,Java,Ruby,Docker 等技术领域.近两年来一直在 COD ...

  6. 也谈谈规范JS代码的几个注意点

    也谈谈规范JS代码的几个注意点 写JS代码差不多也有两年了吧,从刚开始的“初生牛犊不怕虎”乱写一通到后来也慢慢知道去规范一下自己写的代码.这种感觉就像是代码是你的作品,你希望它保持一份不仅干净而且也优 ...

  7. 使用storyboard显示UITableView时,如果不修改系统默认生成的tableView:cellForRowAtIndexPath:方法中的代码,则必须为UITableViewCell注册(填写)重用标识符:identifier.必须要代码方法中的标识符一致.

    CHENYILONG Blog 使用storyboard显示UITableView时,如果不修改系统默认生成的tableView:cellForRowAtIndexPath:方法中的代码,则必须为UI ...

  8. 阿里云代码管理平台 Teambition Codeup(行云)亮相,为企业代码安全护航

    2019杭州云栖大会企业协作与研发效能专场,企业协同平台Teambition负责人齐俊元正式发布阿里云自研的代码管理平台Teambition Codeup(行云),Codeup是一款企业级代码管理产品 ...

  9. 使用java代码动态配置与xml文件结合的方式使用mybatis-generator生成代码配置

    1.使用java代码动态配置与xml文件结合的方式使用mybatis-generator生成代码配置 2.上代码:在resources目录下新建:generatorConfiguration.xml文 ...

随机推荐

  1. DAY8-Python学习笔记

    老样子课有点多,睡觉有点多,玩手机有点多,总结就是事情有点多.Python项目还没找好所以就没上手. 今天学习内容贴几张图...

  2. BZOJ5020 THUWC2017在美妙的数学王国中畅游(LCT)

    明摆着的LCT,问题在于如何维护答案.首先注意到给出的泰勒展开式,并且所给函数求导非常方便,肯定要用上这玩意.容易想到展开好多次达到精度要求后忽略余项.因为x∈[0,1]而精度又与|x-x0|有关,当 ...

  3. 【刷题】BZOJ 2049 [Sdoi2008]Cave 洞穴勘测

    Description 辉辉热衷于洞穴勘测.某天,他按照地图来到了一片被标记为JSZX的洞穴群地区.经过初步勘测,辉辉发现这片区域由n个洞穴(分别编号为1到n)以及若干通道组成,并且每条通道连接了恰好 ...

  4. 【刷题】BZOJ 3529 [Sdoi2014]数表

    Description 有一张n×m的数表,其第i行第j列(1<=i<=n,1<=j<=m)的数值为能同时整除i和j的所有自然数之和.给定a,计算数表中不大于a的数之和. In ...

  5. Vue里边接口访问Post、Get

    原文地址: http://www.cnblogs.com/JimmyBright/p/7356502.html 通常js里面都用ajax来和服务器交换数据,Vue里边当然也可以用ajax,ajax是基 ...

  6. 【ARC075F】Mirror

    Description ​ 给定正整数\(D\),求有多少个正整数\(N\),满足\(rev(N)=N+D\). ​ 其中\(rev(N)\)表示将\(N\)的十进制表示翻转来读得到的数(翻转后忽略前 ...

  7. PHP正则表达式基本语法

    本章主要学习正则表达式的基本语法: 正则表达式就是一个匹配的模式,正则表达式本身也就是一个字符串(有一些语法规则,特殊符号组成) 正则表达式这个字符串一定要在对应的函数中使用才有意义(分割,替换函数结 ...

  8. activiti教程之示例项目activiti-explorer运行_百度经验

    https://jingyan.baidu.com/article/4e5b3e19107ad091901e249e.html

  9. Non-Local Image Dehazing 复现

    本文选自CVPR 2016, 文章链接Dana Berman, Tali Treibitz, Shai Avidan. Non-Local Image Dehazing 复现源码见我的Github 无 ...

  10. sys模块python

    sys模块 1: sys是python自带模块. 利用 import 语句输入sys 模块. 当执行import sys后, python在 sys.path 变量中所列目录中寻找 sys 模块文件. ...