您好,登錄后才能下訂單哦!
# coding:utf-8
# 本地調(diào)用除法運算的形式
class InvalidOperation(Exception):
def __init__(self, message = None):
self.message = message or 'involid operation'
def divide(num1, num2 = 1):
if num2 == 0:
raise InvalidOperation
res = num1 / num2
return res
try:
val = divide(200, 100)
except InvalidOperation as e:
print(e.message)
else:
print(val)
import struct
from io import BytesIO
import socket
class InvalidOperation(BaseException):
def __init__(self, message = None):
self.message = message or 'involid operation'
class MethodProtocol(object):
''''
解讀方法名
'''
def __init__(self, connection):
self.conn = connection
def _read_all(self, size):
"""
幫助我們讀取二進(jìn)制數(shù)據(jù)
:param size: 想要讀取的二進(jìn)制數(shù)據(jù)大小
:return: 二進(jìn)制數(shù)據(jù)bytes
"""
# self.conn
if isinstance(self.conn, BytesIO):
buff = self.conn.read(size)
return buff
else:
# 有時候長度大于每次讀取的長度
have = 0
buff = b''
while have < size:
chunk = self.conn.recv(size - have)
buff += chunk
l = len(chunk)
have += l
if l == 0:
# 表示客戶端已經(jīng)關(guān)閉了
raise EOFError
return buff
def get_method_name(self):
# 讀取字符串長度
buff = self._read_all(4)
length = struct.unpack('!I',buff)[0]
# 讀取字符串
buff = self._read_all(length)
name = buff.decode()
return name
class DivideProtocol(object):
"""
divide過程消息協(xié)議轉(zhuǎn)換工具
"""
def args_encode(self, num1, num2=1):
"""
將原始調(diào)用的請求參數(shù)轉(zhuǎn)換打包成二進(jìn)制消息數(shù)據(jù)
:param num1: int
:param num2: int
:return: bytes 二進(jìn)制消息數(shù)據(jù)
"""
name = 'divide'
# 處理函數(shù)名
buff = struct.pack('!I', 6) # 無符號int
buff += name.encode()
# 處理參數(shù)1
buff2 = struct.pack('!B', 1) # 無符號byte
buff2 += struct.pack('!i', num1)
# 處理參數(shù)2
if num2 != 1:
# 沒有傳參的時候
buff2 += struct.pack('!B', 2)
buff2 += struct.pack('!i', num2)
# 處理參數(shù)邊界和組合成完整數(shù)據(jù)
buff += struct.pack('!I',len(buff2))
buff += buff2
return buff
def _read_all(self, size):
"""
幫助我們讀取二進(jìn)制數(shù)據(jù)
:param size: 想要讀取的二進(jìn)制數(shù)據(jù)大小
:return: 二進(jìn)制數(shù)據(jù)bytes
"""
# self.conn
if isinstance(self.conn, BytesIO):
buff = self.conn.read(size)
return buff
else:
# 有時候長度大于每次讀取的長度
have = 0
buff = b''
while have < size:
chunk = self.conn.recv(size - have)
buff += chunk
l = len(chunk)
have += l
if l == 0:
# 表示客戶端已經(jīng)關(guān)閉了
raise EOFError
return buff
def args_decode(self, connection):
"""
接受調(diào)用請求數(shù)據(jù)病進(jìn)行解析
:param connection: 鏈接請求數(shù)據(jù) socket BytesIO
:return: 因為有多個參數(shù),定義為字典
"""
param_len_map = {
1:4,
2:4,
}
param_fmt_map = {
1:'!i',
2:'!i',
}
param_name_map = {
1: 'num1',
2: 'num2',
}
# 保存用來返回的參數(shù)字典
args = {}
self.conn = connection
# 處理方法的名字,已經(jīng)提前被處理,稍后處理
# 處理消息邊界
# 1) 讀取二進(jìn)制數(shù)據(jù)----read , ------ByteIO.read
# 2) 將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換為python的數(shù)據(jù)類型
buff = self._read_all(4)
length = struct.unpack('!I',buff)[0]
# 記錄已經(jīng)讀取的長度值
have = 0
# 處理第一個參數(shù)
# 解析參數(shù)序號
buff = self._read_all(1)
have += 1
param_seq = struct.unpack('!B', buff)[0]
# 解析參數(shù)值
param_len = param_len_map[param_seq]
buff = self._read_all(param_len)
have += param_len
param_fmt = param_fmt_map[param_seq]
param = struct.unpack(param_fmt,buff)[0]
# 設(shè)置解析后的字典
param_name = param_name_map[param_seq]
args[param_name] = param
if have >= length:
return args
# 處理第二個參數(shù)
# 解析參數(shù)序號
buff = self._read_all(1)
param_seq = struct.unpack('!B', buff)[0]
# 解析參數(shù)值
param_len = param_len_map[param_seq]
buff = self._read_all(param_len)
param_fmt = param_fmt_map[param_seq]
param = struct.unpack(param_fmt, buff)[0]
# 設(shè)置解析后的字典
param_name = param_name_map[param_seq]
args[param_name] = param
return args
def result_encode(self, result):
"""
將原始結(jié)果數(shù)據(jù)轉(zhuǎn)換為消息協(xié)議二進(jìn)制數(shù)據(jù)
:param result:
:return:
"""
if isinstance(result,float):
# 處理返回值類型
buff = struct.pack('!B', 1)
buff += struct.pack('!f', result)
return buff
else:
buff = struct.pack('!B', 2)
# 處理返回值
length = len(result.message)
# 處理字符串長度
buff += struct.pack('!I', length)
buff += result.message.encode()
return buff
def result_decode(self, connection):
"""
將返回值消息數(shù)據(jù)轉(zhuǎn)換為原始返回值
:param connection: socket BytesIo
:return: float InvalidOperation對象
"""
self.conn = connection
# 處理返回值類型
buff = self._read_all(1)
result_type = struct.unpack('!B', buff)[0]
if result_type == 1:
#正常情況
buff = self._read_all(4)
val = struct.unpack('!f', buff)[0]
return val
else:
buff = self._read_all(4)
length = struct.unpack('!I', buff)[0]
# 讀取字符串
buff = self._read_all(length)
message = buff.decode(buff)
return InvalidOperation(message)
class Channel(object):
"""
用于客戶端建立網(wǎng)絡(luò)鏈接
"""
def __init__(self, host, port):
self.host = host
self.port = port
def get_connection(self):
"""
獲取鏈接對象
:return: 與服務(wù)器通訊的socket
"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.host, self.port))
return sock
class Server(object):
"""
RPC服務(wù)器
"""
def __init__(self, host, port, handlers):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 地址復(fù)用
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.host = host
self.port = port
# 綁定地址
sock.bind((self.host, self.port))
# 因為在啟動的方法中才開啟監(jiān)聽,所以不在此處開啟
# sock.listen(128)
self.sock = sock
self.handlers = handlers
def serve(self):
"""
開啟服務(wù)器運行,提供RPC服務(wù)
:return:
"""
# 開啟服務(wù)器的監(jiān)聽,等待客戶端的鏈接請求
self.sock.listen(128)
print("服務(wù)器開啟監(jiān)聽,ip地址為%s,port為%d..." % (self.host,self.port))
while True:
# 不斷的接收客戶端的鏈接請求
client_sock, client_addr = self.sock.accept()
print("與客戶端%s建立連接" % str(client_addr))
# 交個ServerStub,完成客戶端的具體的RPC的調(diào)用請求
stub = ServerStub(client_sock, self.handlers)
try:
while True:
# 不斷的接收
stub.process()
except EOFError:
# 表示客戶端關(guān)閉了連接
print('客戶端關(guān)閉了連接')
client_sock.close()
class ClientStub(object):
"""
用來幫助客戶端完成遠(yuǎn)程過程調(diào)用 RPC調(diào)用
stub = ClientStub()
stub.divide(200, 100)
"""
def __init__(self, channel):
self.channel = channel
self.conn = self.channel.get_connection()
def divide(self, num1, num2 = 1):
# 將調(diào)用的參數(shù)打包成消息協(xié)議的數(shù)據(jù)
proto = DivideProtocol()
args = proto.args_encode(num1, num2)
# 將消息數(shù)據(jù)通過網(wǎng)絡(luò)發(fā)送給服務(wù)器
self.conn.sendall(args)
# 接受服務(wù)器返回的消息數(shù)據(jù),并進(jìn)行解析
result = proto.result_decode(self.conn)
# 將結(jié)果之(正常float 或 異常InvalidOperation)返回給客戶端
if isinstance(result,float):
return result
else:
raise result
class ServerStub(object):
"""
服務(wù)端存根
幫助服務(wù)端完成遠(yuǎn)端過程調(diào)用
"""
def __init__(self, connection, handlers):
"""
:param connection: 與客戶端的鏈接
:param handlers: 真正的本地函數(shù)路由
此處不以map的形式處理,實現(xiàn)類的形式
class Handler:
@staticmethod
def divide():
pass
@staticmethod
def add():
pass
"""
self.conn = connection
self.method_proto = MethodProtocol(self.conn)
self.process_map = {
'divide': self._process_divide,
'add': self._process_add
}
self.handlers = handlers
def process(self):
"""
當(dāng)服務(wù)端接受了客戶的鏈接,建立好鏈接后,完成遠(yuǎn)端調(diào)用的處理
:return:
"""
# 接收消息數(shù)據(jù),并解析方法的名字
name = self.method_proto.get_method_name()
# 根據(jù)解析獲得的方法名,調(diào)用相應(yīng)的過程協(xié)議,接收并解析消息數(shù)據(jù)
self.process_map[name]()
def _process_divide(self):
"""
處理除法過程調(diào)用
:return:
"""
proto = DivideProtocol()
args = proto.args_decode(self.conn)
# args = {'num1':xxx, 'num2':xxx}
# 除法過程的本地調(diào)用------------------->>>>>>>>>
# 將本地調(diào)用過程的返回值(包括可能的異常)打包成消息協(xié)議的數(shù)據(jù),通過網(wǎng)絡(luò)返回給客戶端
try:
val = self.handlers.divide(**args)
except InvalidOperation as e:
ret_message = proto.result_encode(e)
else:
ret_message = proto.result_encode(val)
self.conn.sendall(ret_message)
def _process_add(self):
"""
處理加法過程調(diào)用
此方法暫時不識閑
:return:
"""
pass
if __name__ == '__main__':
# 目的:消息協(xié)議測試,模擬網(wǎng)絡(luò)傳輸
# 構(gòu)造消息數(shù)據(jù)
proto = DivideProtocol()
# 測試一
# divide(200,100)
# message = proto.args_encode(200,100)
# 測試二
message = proto.args_encode(200)
conn = BytesIO()
conn.write(message)
conn.seek(0)
# 解析消息數(shù)據(jù)
method_proto = MethodProtocal(conn)
name = method_proto.get_method_name()
print(name)
args = proto.args_decode(conn)
print(args)
from services import InvalidOperation
from services import Server
class Handlers:
@staticmethod
def divide(num1, num2 = 1):
if num2 == 0:
raise InvalidOperation('ck_god_err')
val = num1/num2
return val
if __name__ == '__main__':
# 開啟服務(wù)器
_server = Server('127.0.0.1', 8000, Handlers)
_server.serve()
ffrom services import ClientStub
from services import Channel
from services import InvalidOperation
# 創(chuàng)建與服務(wù)器的連接
channel = Channel('127.0.0.1', 8000)
# 創(chuàng)建用于rpc調(diào)用的工具
stub = ClientStub(channel)
# 進(jìn)行調(diào)用
for i in range(5):
try:
# val = stub.divide(i * 100,100)
# val = stub.divide(i * 100)
val = stub.divide( 100, 0)
except InvalidOperation as e:
print(e.message)
else:
print(val)
class ThreadServer(object):
"""
多線成RPC服務(wù)器
"""
def __init__(self, host, port, handlers):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# 地址復(fù)用
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.host = host
self.port = port
# 綁定地址
sock.bind((self.host, self.port))
# 因為在啟動的方法中才開啟監(jiān)聽,所以不在此處開啟
# sock.listen(128)
self.sock = sock
self.handlers = handlers
def serve(self):
"""
開啟服務(wù)器運行,提供RPC服務(wù)
:return:
"""
# 開啟服務(wù)器的監(jiān)聽,等待客戶端的鏈接請求
self.sock.listen(128)
print("服務(wù)器開啟監(jiān)聽,ip地址為%s,port為%d..." % (self.host,self.port))
while True:
# 不斷的接收客戶端的鏈接請求
client_sock, client_addr = self.sock.accept()
print("與客戶端%s建立連接" % str(client_addr))
t = threading.Thread(target= self.handle, args=(client_sock,))
t.start()
# 子線程函數(shù)
def handle(self,client_sock):
"""
子線程調(diào)用的方法,用來處理一個客戶段的請求
:return:
"""
# 交個ServerStub,完成客戶端的具體的RPC的調(diào)用請求
stub = ServerStub(client_sock, self.handlers)
try:
while True:
# 不斷的接收
stub.process()
except EOFError:
# 表示客戶端關(guān)閉了連接
print('客戶端關(guān)閉了連接')
client_sock.close()
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。