Thrift遠程通信教程
目標:實現Python通過Thrift實現服務器-客戶端相互通信。
前期準備
- thrift.exe可執行文件
- 官方.thrift樣例文件
無加密本地通信版本
修改.thrift文件
如圖所示:
利用thrift腳本創建gen-py文件夾
在當前目錄打開一個命令行工具:
在命令行工具裏敲入以下命令:
.\thrift -r --gen py ./tutorial.thrift
其中.\thrift
是打開當前目錄下的名字叫thrift的程序,後面是參數。
通過直接打開的方式可以省去配置環境變量的過程。
安裝python的thrift庫
在任意目錄下的命令行工具裏敲入以下命令即可安裝:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple thrift
有的同學的電腦安裝到一半可能會斷掉,這是因爲訪問外網太慢導致的,可以多執行幾次,只要有一次安裝成功即可。
修改PythonServer.py文件
在原來什麼都不動的情況下,在CalculatorHandler 末尾加入一個剛纔定義好的函數,進行這個函數的實現方法。
def echo(self, msg, cnt):
print("%s,%d"%(msg,cnt))
return cnt + 1
修改PythonClient.py文件
在PythonClient.py 中添加如下代碼
flag=client.echo("hello server!",2020)
print("hello server!,%d"%flag)
運行
先運行Server端,再運行Client端。
加密本地通信版本
前期準備
相較於無加密版本需要有openssl軟件來生成密鑰和證書。
安裝openssl
注意,此處安裝目錄爲C:\OpenSSL-Win64
。
測試openssl
打開命令行工具,輸入:
C:\OpenSSL-Win64\bin\openssl version
如果出現版本號就說明配置成功。
生成私鑰和證書
- 在你想要存放密鑰和證書的目錄下打開命令行,輸入如下命令
C:\OpenSSL-Win64\bin\openssl req -new -x509 -days 365 -nodes -out cert.pem -keyout key.pem
- 國家輸入
CN
- 不重要的可以不填,直接按回車即可跳過。
- 到服務器這一個的時候填入本機地址:
127.0.0.1
。 - 剩下的繼續回車跳過。
- 可以看到目錄下已經有了私鑰和證書,其中"cert.pem"是客戶端用的公鑰(證書),"key.pem"是服務器用的私鑰。
修改python文件,使用證書加密通信
利用thrift生成gen-py文件夾:
Server端:
將1處的代碼修改爲
from thrift.transport import TSSLSocket
將2處的代碼修改爲
transport = TSSLSocket.TSSLServerSocket(host="127.0.0.1", port=9090, certfile='cert.pem', keyfile='key.pem')
注意:其中certfile,keyfile記得替換爲你生成公私鑰的名字。
Client端
將1處的代碼修改爲
from thrift.transport import TSSLSocket
將2處的代碼修改爲
transport = TSSLSocket.TSSLSocket(host="127.0.0.1", port=9090, ca_certs='cert.pem')
注意:其中ca_certfile記得替換爲你生成公私鑰的名字。
Server完整代碼
import sys
sys.path.append('./gen-py')
from tutorial import Calculator
from tutorial.ttypes import InvalidOperation, Operation
from shared.ttypes import SharedStruct
from thrift.transport import TSSLSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class CalculatorHandler:
def __init__(self):
self.log = {}
def ping(self):
print('ping()')
def add(self, n1, n2):
print('add(%d,%d)' % (n1, n2))
return n1 + n2
def calculate(self, logid, work):
print('calculate(%d, %r)' % (logid, work))
if work.op == Operation.ADD:
val = work.num1 + work.num2
elif work.op == Operation.SUBTRACT:
val = work.num1 - work.num2
elif work.op == Operation.MULTIPLY:
val = work.num1 * work.num2
elif work.op == Operation.DIVIDE:
if work.num2 == 0:
raise InvalidOperation(work.op, 'Cannot divide by 0')
val = work.num1 / work.num2
else:
raise InvalidOperation(work.op, 'Invalid operation')
log = SharedStruct()
log.key = logid
log.value = '%d' % (val)
self.log[logid] = log
return val
def getStruct(self, key):
print('getStruct(%d)' % (key))
return self.log[key]
def zip(self):
print('zip()')
def echo(self, msg, cnt):
print("%s,%d" % (msg, cnt))
return cnt + 1
if __name__ == '__main__':
handler = CalculatorHandler()
processor = Calculator.Processor(handler)
transport = TSSLSocket.TSSLServerSocket(host="127.0.0.1", port=9090, certfile='cert.pem', keyfile='key.pem')
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
# You could do one of these for a multithreaded server
# server = TServer.TThreadedServer(
# processor, transport, tfactory, pfactory)
# server = TServer.TThreadPoolServer(
# processor, transport, tfactory, pfactory)
print('Starting the server...')
server.serve()
print('done.')
Client完整代碼
import sys
import glob
sys.path.append('./gen-py')
from tutorial import Calculator
from tutorial.ttypes import InvalidOperation, Operation, Work
from thrift import Thrift
from thrift.transport import TSSLSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
def main():
# Make socket
transport = TSSLSocket.TSSLSocket(host="127.0.0.1", port=9090, ca_certs='cert.pem')
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = Calculator.Client(protocol)
# Connect!
transport.open()
client.ping()
print('ping()')
sum_ = client.add(1, 1)
print('1+1=%d' % sum_)
work = Work()
work.op = Operation.DIVIDE
work.num1 = 1
work.num2 = 0
try:
quotient = client.calculate(1, work)
print('Whoa? You know how to divide by zero?')
print('FYI the answer is %d' % quotient)
except InvalidOperation as e:
print('InvalidOperation: %r' % e)
work.op = Operation.SUBTRACT
work.num1 = 15
work.num2 = 10
diff = client.calculate(1, work)
print('15-10=%d' % diff)
log = client.getStruct(1)
print('Check log: %s' % log.value)
flag = client.echo("hello server!", 2020)
print("hello server!,%d" % flag)
# Close!
transport.close()
if __name__ == '__main__':
try:
main()
except Thrift.TException as tx:
print('%s' % tx.message)
運行結果:
加密遠程通信版本
前期準備
- 有效的證書
修改代碼
已經有了使用證書通信的經驗,這次會容易得多。
首先PythonServer.py文件是沒有用的,因爲我們要連接的服務器在網絡上,網絡端是已經寫好的,我們只需要寫好客戶端去連接即可,也就是PythonClient.py。
其次需要把要連接的服務器IP改成真實的服務器IP,把證書改成你的證書即可。