目录
一、RPC
远程过程调用(Remote Procedure Call,RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。
二、Pyro4
Pyro4 是一个 Python 库,用于实现网络上的远程过程调用(RPC)。它允许你构建分布式应用程序,其中对象可以在网络中的不同机器上调用彼此的方法,就好像它们都在同一台机器上运行一样。Pyro4 使得通过网络透明地调用远程对象变得简单,而不需要担心底层网络通信的细节。
使用 Pyro4,你可以定义一个服务端程序,该程序暴露一个或多个Python对象作为远程服务。然后,客户端程序可以远程调用这些对象的方法,就像它们是本地对象一样。Pyro4 处理所有的序列化和通信,使得分布式编程变得更加简单。
三、CRC
CRC(Cyclic Redundancy Check)循环冗余校验是一种用于检测数据传输或存储中的错误的校验码技术,用于确保数据的完整性。
本文章只实现了计算出发送端待发送的信息。
四、Server
crc_SendCode.cpp代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 判断是否为Windows平台,用于定义导出函数的宏
#ifdef _WIN32
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif
// 用extern "C"告诉C++编译器这部分代码要以C语言的方式进行链接
extern "C" {
// 声明两个将要导出的函数
EXPORT char* calculate_crc(const char *original_message, const char *generator);
EXPORT void free_memory(char *ptr);
}
// 定义divide函数,用于执行除法运算,模拟CRC的计算过程
EXPORT void divide(char *dividend, const char *divisor, char *remainder) {
int len_divisor = strlen(divisor); // 计算除数的长度
int len_dividend = strlen(dividend); // 计算被除数的长度
// 将被除数的前len_divisor个字符复制到余数中,并添加字符串结束符
strncpy(remainder, dividend, len_divisor);
remainder[len_divisor] = '\0';
// 对每个被除数中的位进行迭代
for (int i = 0; i <= len_dividend - len_divisor; ++i) {
// 如果余数的首位是1,则进行异或运算,否则不变
if (remainder[0] == '1') {
for (int j = 1; j < len_divisor; ++j) {
remainder[j] = ((remainder[j] == divisor[j]) ? '0' : '1');
}
}
// 将余数左移一位,并用被除数的下一个字符填充最右边的位
memmove(remainder, remainder + 1, len_divisor - 1);
remainder[len_divisor - 1] = dividend[i + len_divisor];
remainder[len_divisor] = '\0';
}
}
// 定义calculate_crc函数,用于计算CRC校验码,并将其附加到原始消息的末尾
EXPORT char* calculate_crc(const char *original_message, const char *generator) {
int message_len = strlen(original_message); // 计算原始消息的长度
int generator_len = strlen(generator); // 计算生成多项式的长度
// 分配足够的空间来存储扩展后的消息,并在末尾添加0
char *extended_message = static_cast<char*>(malloc(message_len + generator_len));
strcpy(extended_message, original_message);
memset(extended_message + message_len, '0', generator_len - 1);
extended_message[message_len + generator_len - 1] = '\0';
// 分配空间来存储CRC校验码
char *crc = static_cast<char*>(malloc(generator_len));
// 调用divide函数计算CRC校验码
divide(extended_message, generator, crc);
// 分配空间来存储最终消息(原始消息+校验码)
char *final_message = static_cast<char*>(malloc(message_len + generator_len));
strcpy(final_message, original_message);
strcat(final_message, crc); // 将校验码附加到原始消息的末尾
// 释放不再需要的内存空间
free(extended_message);
free(crc);
// 返回包含校验码的最终消息
return final_message;
}
使用 g++ -shared -o output.dll input.cpp 命令创建一个 Windows 动态链接库(DLL)
g++:调用 GCC 编译器中的 G++ 编译器。
-shared:告诉编译器生成一个共享库(在 Windows 上是 DLL,在 Linux 上是 so 文件)。
-o output.dll:指定输出文件的名称,这里是 output.dll。-o选项后面紧跟着的是生成的文件名。
input.cpp:源代码文件,编译器将会编译这个文件生成 DLL。
crc_Server.py代码
import Pyro4
from ctypes import cdll, c_char_p, create_string_buffer
# 使用Pyro4.expose装饰器,使得类中的方法可以被远程调用
@Pyro4.expose
class CRCServer(object):
# 定义一个crc_check方法,用于进行CRC校验
def crc_check(self, original_message, generator):
# 加载编译好的dll文件
lib = cdll.LoadLibrary(r'D:\Programming\Works\CN\RMI\test4\output.dll')
# 设置dll中calculate_crc函数的返回类型和参数类型
lib.calculate_crc.restype = c_char_p
lib.calculate_crc.argtypes = [c_char_p, c_char_p]
# 将原始消息和生成多项式字符串编码为字节串
original_message_bytes = original_message.encode('utf-8')
generator_bytes = generator.encode('utf-8')
# 创建字符串缓冲区,这些缓冲区将被传递给dll函数
original_message = create_string_buffer(original_message_bytes)
generator = create_string_buffer(generator_bytes)
# 调用dll函数,计算并返回最终的CRC校验码
final_message = lib.calculate_crc(original_message, generator)
return final_message
# 定义启动服务器的函数
def start_server():
daemon = Pyro4.Daemon() # 创建Pyro守护进程
ns = Pyro4.locateNS(host="localhost", port=9090) #定位到运行在本机9090端口的Pyro名称服务
uri = daemon.register(CRCServer) # 将CRCServer类注册为Pyro对象
ns.register("example.service_uri", uri) #使用静态名称"example.service_uri"注册Pyro对象
print("Ready. Object uri =", uri) # 打印Pyro对象的URI,供客户端连接使用
daemon.requestLoop() # 进入Pyro的请求循环,等待客户端调用
# 程序的主入口
if __name__ == "__main__":
start_server() # 调用启动服务器的函数
五、 Client
crc_Client.py
import tkinter as tk
from tkinter import messagebox
import Pyro4
import base64
# 定义函数,通过Pyro4调用远程CRC校验服务
def crc_check(original_message, generator):
ns = Pyro4.locateNS(host="localhost", port=9090) # 定位到运行在本机9090端口的Pyro名称服务
uri = ns.lookup("example.service_uri") # 从名称服务中查找对象的URI
crc_server = Pyro4.Proxy(uri) # 创建代理对象,用于远程调用
return crc_server.crc_check(original_message, generator) # 调用远程方法
# 定义提交按钮的事件处理函数
def on_submit():
original_message = entry_original_message.get() # 获取用户输入的原始消息
generator = entry_generator.get() # 获取用户输入的生成多项式
result = crc_check(original_message, generator) # 调用远程CRC校验服务
final_message = base64.b64decode(result['data']).decode('utf-8') # 对返回的base64编码数据进行解码
messagebox.showinfo("待发送的信息:", final_message) # 弹出消息框显示处理后的信息
# 创建主窗口
root = tk.Tk()
root.title("CRC") # 设置窗口标题为"CRC"
# 创建并放置标签和输入框
tk.Label(root, text="原始消息:").grid(row=0, column=0) # 创建标签并放置在网格的位置
entry_original_message = tk.Entry(root) # 创建文本输入框用于输入原始消息
entry_original_message.grid(row=0, column=1) # 将输入框放置在网格的位置
tk.Label(root, text="生成多项式:").grid(row=1, column=0) # 创建标签并放置在网格的位置
entry_generator = tk.Entry(root) # 创建文本输入框用于输入生成多项式
entry_generator.grid(row=1, column=1) # 将输入框放置在网格的位置
# 创建并放置提交按钮
submit_button = tk.Button(root, text="提交", command=on_submit) # 创建按钮并设置点击时调用的函数
submit_button.grid(row=2, column=0, columnspan=2) # 将按钮放置在网格的位置,并跨越两列
# 启动GUI事件循环,等待用户操作
root.mainloop()
六、运行演示
七、参考资料
https://zh.wikipedia.org/wiki/%E9%81%A0%E7%A8%8B%E9%81%8E%E7%A8%8B%E8%AA%BF%E7%94%A8
Pyro4 · PyPI