我们日常上网看新闻,刷抖音的背后都是千千万万的服务器在无时无刻的对外提供服务来响应来自客户端的请求。本文将详细介绍利用Python3的xmlrpc来写一个服务端和客户端
一:xmlrpc简介
XML-RPC的全称是XML Remote Procedure Call,即XML(标准通用标记语言下的一个子集)远程过程调用。它是一套允许运行在不同操作系统、不同环境的程序实现基于Internet过程调用的规范和一系列的实现。这种远程过程调用使用http作为传输协议,XML作为传送信息的编码格式。Xml-Rpc的定义尽可能的保持了简单,但同时能够传送、处理、返回复杂的数据结构。这个过程也被大家称为“分布式计算”。 xmlrpc:使用http协议作为传输协议的rpc机制。
二:xmlrpc服务端
xmlrpc服务端主要要实现两个代码功能,一是提供建立服务的接口,二是提供注册函数的接口。建立服务很好理解,就是基于ip和端口建立对外服务。注册函数决定了该服务端对外提供什么样的服务。我们先看代码
# -*- coding: utf-8 -*-
import json
import os
from urllib import request
import urllib
from socketserver import ThreadingMixIn
from xmlrpc.server import SimpleXMLRPCServer
from xmlrpc.server import SimpleXMLRPCRequestHandler
class Processor(object):
@classmethod
def aPLusb(cls,a,b):
return a+b
class Listener(object):
"""用来描述监听服务器"""
ListenerIP = ''
ListenerPort = ''
Server = ''
# 读取ip和port
@classmethod
def LoadListener(cls):
cls.ListenerIP = '10.56.107.133'
cls.ListenerPort = 5000
print('ListenerAddr: ' + cls.ListenerIP + ':' + str(cls.ListenerPort))
# 指定访问RPC服务器的URL前缀
class RequestHandler(SimpleXMLRPCRequestHandler): rpc_paths = ('/RPC2',)
# 创建服务器
class MyXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
daemon_threads =True
cls.Server = MyXMLRPCServer((cls.ListenerIP, cls.ListenerPort),
requestHandler=RequestHandler, allow_none=True)
# 支持列出服务器支持的所有方法
cls.Server.register_introspection_functions()
# 注册RPC函数
@classmethod
def RegisterFunction(cls, Func, FuncDesc):
cls.Server.register_function(Func, FuncDesc)
# 启动XMLRPC服务器
@classmethod
def StartListener(cls):
cls.Server.serve_forever()
if __name__ == '__main__':
# 注册RPC函数
Listener = Listener()
Listener.LoadListener()
Processor = Processor()
Listener.RegisterFunction(Processor.aPLusb, 'aPLusb')
# 开始监听
Listener.StartListener()
上述服务端实现的功能很简单就是对外提供一个加法运算的服务。
首先建立一个类MyXMLRPCServer,实例化该类的时候传入ip和端口参数,请求的句柄。
# 指定访问RPC服务器的URL前缀
class RequestHandler(SimpleXMLRPCRequestHandler): rpc_paths = ('/RPC2',)
# 创建服务器
class MyXMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
daemon_threads =True
cls.Server = MyXMLRPCServer((cls.ListenerIP, cls.ListenerPort),
requestHandler=RequestHandler, allow_none=True)
接下来我们写一个接口来注册函数
# 注册RPC函数
@classmethod
def RegisterFunction(cls, Func, FuncDesc):
cls.Server.register_function(Func, FuncDesc)
然后实现一个加法运算函数,并将该函数注册进去
class Processor(object):
@classmethod
def aPLusb(cls,a,b):
return a+b
# 注册RPC函数
Listener.RegisterFunction(Processor.aPLusb, 'aPLusb')
最后我们启动服务
# 启动XMLRPC服务器
@classmethod
def StartListener(cls):
cls.Server.serve_forever()
至此服务端程序完成
三:xmlrpc客户端
客户端就是和人交互的窗口,向服务端发送请求来获取自己想要的内容。为了更加具体的显示交互的效果,本次借助cmd模块实现了一个小的交互界面。先看客户端的代码
import xmlrpc.client
import sys
import json
import cmd
import urllib
from docopt import docopt, DocoptExit
from colorama import init, Fore
import time
class ServerInfo(object):
"""用来定义服务端信息"""
ServerIP = ''
ServerPort = ''
@classmethod
def LoadServerInfo(cls):
cls.ServerIP = '10.56.107.133'
cls.ServerPort = '5000'
@classmethod
def GetServerAddr(cls):
"""用来获取服务器地址"""
return (cls.ServerIP, cls.ServerPort)
@classmethod
def GetServerUrl(cls):
if (cls.ServerIP == '') or (cls.ServerPort == ''):
return ''
return xmlrpc.client.ServerProxy('http://' + cls.ServerIP
+ ':' + cls.ServerPort
)
# 定义一个装饰函数,用来解析命令行
def docopt_cmd(func):
"""
This decorator is used to simplify the try/except block and pass the result
of the docopt parsing to the called action.
"""
def fn(self, arg):
try:
opt = docopt(fn.__doc__, arg)
except DocoptExit as e:
# The DocoptExit is thrown when the args do not match.
# We print a message to the user and the usage block.
print('Invalid Command!')
print(e)
return
except SystemExit:
# The SystemExit exception prints the usage for --help
# We do not need to do the print here.
return
return func(self, opt)
fn.__name__ = func.__name__
fn.__doc__ = func.__doc__
fn.__dict__.update(func.__dict__)
return fn
class MyInteractive (cmd.Cmd):
"""该类用于提供命令行界面并和服务器进行交互"""
# intro是欢迎界面
intro = """***********************************
======Welcome to csdn ftz server-client!=======
(type help for a list of commands.)
***********************************"""
# prompt是提示符
prompt = 'ftz_csdn> '
@docopt_cmd
def do_plusCaculate(self, arg):
"""Usage: plusCaculate --a=<int> --b=<int>
Options:
--a=<int> para 1
--b=<int> para 2
"""
s = ServerInfo().GetServerUrl()
a = int(arg['--a'])
b = int(arg['--b'])
print(s.aPLusb(a,b))
def do_quit(self, arg):
"""Quits out of Interactive Mode."""
sys.exit()
def emptyline(self):
print('ftz_csdn> ')
if __name__ == '__main__':
#init(autoreset=True)
ServerInfo().LoadServerInfo()
MyInteractive().cmdloop()
程序运行的效果:
可以在窗口中敲入help或者?来提示客户端支持的命令
下面来讲讲具体的实现。
首先用docopt实现了一个装饰函数,docopt基于长久以来在帮助信息和手册中描述程序接口的约定,其接口描述是形式化的帮助信息。它能够根据命令行程序中定义的接口描述,来自动生成解析器。利用该装饰器我们可以很轻松来获取命令行输入的参数和显示帮助信息。
下面来讲讲客户端怎么向服务端发送请求。调用xmlrpc.client的ServerProxy接口,传入url参数即可获取服务端的句柄,利用该句柄调用服务端注册的函数即可实现对应的功能。
@classmethod
def GetServerUrl(cls):
if (cls.ServerIP == '') or (cls.ServerPort == ''):
return ''
return xmlrpc.client.ServerProxy('http://' + cls.ServerIP
+ ':' + cls.ServerPort
)
写客户端需要注意的是,MyInteractive类里支持的命令格式如下:
def do_xxxxx(self, arg):
命令的函数命名是固定的前缀do_,在该函数下面实现要交互的内容。
可以看到plusCaculate命令提供了两个参数,a和b(传入的时候要遵从代码给的格式,具体的格式可以根据个人的喜好来设计)
如果想要客户端一直运行就调用cmd的cmdloop()这样客户端就一直停留在前台。本次实现也写了一个客户端退出的命令
def do_quit(self, arg):
"""Quits out of Interactive Mode."""
sys.exit()
最后总的交互效果如下:
至此我们利用xmlrpc实现了一个简单的前后端服务。码字不易,代码调试不易,如果本文给你提供了帮助的可以点个赞,给个关注。