python網絡基礎

代碼地址:https://github.com/brandon-rhodes/fopnp/tree/m/html

本書在kindle顯示爲亂碼。python

背景:git

https://github.com/brandon-rhodes/fopnp/tree/m/playgroundgithub

如下每臺機器能夠用docker容器實現。docker

 

 

 

RPC

RPC簡介

遠程過程調用(Remote Procedure Call,RPC)是IPC(inter-process communication)的一種,該協議容許運行程序調用另外一地址空間(一般是共享網絡的另外一計算機)的子程序,就像調用本地過程同樣。面向對象中常稱爲遠程方法調用RMI(remote method invocation)或遠程方法調用。 多用於分佈式計算。調用一般比本地慢、可靠性也差些。json

 

RPC的做用:數組

  • 不改變代碼進行水平擴展
  • 獲取網絡上磁盤或主機特定的信息。

最初rpc主要用c語言開發,基於二進制傳輸,相似python的struct,須要知道定義的頭文件,且容易發生錯誤和crash。隨着硬件和網絡的加速,逐漸改成可讀性更好的格式。安全

老協議發送的數據可能以下:服務器

0, 0, 0, 1, 64, 36, 0, 0, 0, 0, 0, 0

新協議不須要頭文件:網絡

<params>
<param><value><i4>41</i4></value></param>
<param><value><double>10.</double></value></param>
</params>

JSON則更加簡化。

[1, 10.0]

 

 

RPC協議特殊的地方:

  • 弱語義:調用的語義不強,含義由API本身肯定。基礎數據類型:整型、浮點型、字符串、列表
  • 調用方法,可是不對方法進行定義。
  • 調用和普通方法或函數相似。

RPC的特色以下:

  • 傳遞的數據類型有限。只支持位置參數,由於須要跨語言。 通常只支持數值和字符串、序列或列表、結構體或聯合數組。 操做系統相關的文件,live socket、shared memory一般不能傳遞。
  • 異常處理:通常要定義錯誤信息,而不是簡單地打印堆棧。 由於客戶端未必有服務器端的代碼,打印出這些堆棧意義不大,反而有可能暴露安全問題。
  • 有些內省支持,以查看支持的的調用及參數。python由於是動態類型,內省支持較差。
  • 有些有尋址支持
  • 有些有認證、訪問控制 ,不過大多仍是基於HTTP。

 

XML-RPC

一般不會使用XML-RPC,由於它比較笨重。由於它是首批基於HTTP的RPC協議,python提供了內置支持。

標準參見:http://xmlrpc.scripting.com/spec.html。
數據類型: int ;  float ;  unicode ;  list ;  dict with  unicode keys; with nonstandard extensions,  datetime
and  None
庫:xmlrpclib ,  SimpleXMLRPCServer ,  DocXMLRPCServer

xmlrpc_server.py

#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter18/xmlrpc_server.py
# XML-RPC server

import operator, math
from xmlrpc.server import SimpleXMLRPCServer
from functools import reduce

def main():
    server = SimpleXMLRPCServer(('127.0.0.1', 7001))
    server.register_introspection_functions()
    server.register_multicall_functions()
    server.register_function(addtogether)
    server.register_function(quadratic)
    server.register_function(remote_repr)
    print("Server ready")
    server.serve_forever()

def addtogether(*things):
    """Add together everything in the list `things`."""
    return reduce(operator.add, things)

def quadratic(a, b, c):
    """Determine `x` values satisfying: `a` * x*x + `b` * x + c == 0"""
    b24ac = math.sqrt(b*b - 4.0*a*c)
    return list(set([ (-b-b24ac) / 2.0*a,
                      (-b+b24ac) / 2.0*a ]))

def remote_repr(arg):
    """Return the `repr()` rendering of the supplied `arg`."""
    return arg

if __name__ == '__main__':
    main()

客戶端使用內省:

 

#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter18/xmlrpc_introspect.py
# XML-RPC client

import xmlrpc.client

def main():
    proxy = xmlrpc.client.ServerProxy('http://127.0.0.1:7001')

    print('Here are the functions supported by this server:')
    for method_name in proxy.system.listMethods():

        if method_name.startswith('system.'):
            continue

        signatures = proxy.system.methodSignature(method_name)
        if isinstance(signatures, list) and signatures:
            for signature in signatures:
                print('{0}({1})'.format(method_name, signature))
        else:
            print('{0}(...)'.format(method_name,))

        method_help = proxy.system.methodHelp(method_name)
        if method_help:
            print('  ', method_help)

if __name__ == '__main__':
    main()

客戶端調用:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter18/xmlrpc_client.py
# XML-RPC client

import xmlrpc.client

def main():
    proxy = xmlrpc.client.ServerProxy('http://127.0.0.1:7001')
    print(proxy.addtogether('x', 'ÿ', 'z'))
    print(proxy.addtogether(20, 30, 4, 1))
    print(proxy.quadratic(2, -4, 0))
    print(proxy.quadratic(1, 2, 1))
    print(proxy.remote_repr((1, 2.0, 'three')))
    print(proxy.remote_repr([1, 2.0, 'three']))
    print(proxy.remote_repr({'name': 'Arthur',
                             'data': {'age': 42, 'sex': 'M'}}))
    print(proxy.quadratic(1, 0, 1))

if __name__ == '__main__':
    main()

執行結果:

# python3 xmlrpc_client.py
x?z
55
[0.0, 8.0]
[-1.0]
[1, 2.0, 'three']
[1, 2.0, 'three']
{'name': 'Arthur', 'data': {'age': 42, 'sex': 'M'}}
Traceback (most recent call last):
  File "xmlrpc_client.py", line 22, in <module>
    main()
  File "xmlrpc_client.py", line 19, in main
    print(proxy.quadratic(1, 0, 1))
  File "/opt/python3.5/lib/python3.5/xmlrpc/client.py", line 1091, in __call__
    return self.__send(self.__name, args)
  File "/opt/python3.5/lib/python3.5/xmlrpc/client.py", line 1431, in __request
    verbose=self.__verbose
  File "/opt/python3.5/lib/python3.5/xmlrpc/client.py", line 1133, in request
    return self.single_request(host, handler, request_body, verbose)
  File "/opt/python3.5/lib/python3.5/xmlrpc/client.py", line 1149, in single_request
    return self.parse_response(resp)
  File "/opt/python3.5/lib/python3.5/xmlrpc/client.py", line 1321, in parse_response
    return u.close()
  File "/opt/python3.5/lib/python3.5/xmlrpc/client.py", line 654, in close
    raise Fault(**self._stack[0])
xmlrpc.client.Fault: <Fault 1: "<class 'ValueError'>:math domain error">

​ 一次傳遞多個調用

 

#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter18/xmlrpc_multicall.py
# XML-RPC client performing a multicall

import xmlrpc.client

def main():
    proxy = xmlrpc.client.ServerProxy('http://127.0.0.1:7001')
    multicall = xmlrpc.client.MultiCall(proxy)
    multicall.addtogether('a', 'b', 'c')
    multicall.quadratic(2, -4, 0)
    multicall.remote_repr([1, 2.0, 'three'])
    for answer in multicall():
        print(answer)

if __name__ == '__main__':
    main()

​能夠在服務器端的日誌看到只有一條記錄。SimpleXMLRPCServer不支持認證和TLS安全機制等。

實際往返的數據,對 quadratic()而言以下:

 

<?xml version='1.0'?>
<methodCall>
    <methodName>quadratic</methodName>
    <params>
        <param>
            <value><int>2</int></value>
        </param>
        <param>
            <value><int>-4</int></value>
        </param>
        <param>
            <value><int>0</int></value>
        </param>
    </params>
</methodCall>


<?xml version='1.0'?>
<methodResponse>
    <params>
        <param>
            <value><array><data>
            <value><double>0.0</double></value>
            <value><double>8.0</double></value>
            </data></array></value>
        </param>
    </params>
</methodResponse>

 

JSON-RPC

標準參見:  http://json-rpc.org/wiki/specification
數據類型: int ;  float ;  unicode ;  list ;  dict with  unicode keys;  None
庫:many third-party, including  jsonrpclib

python標準庫不支持jsonrpc。

jsonrpclib是流行的 jsonrpc庫之一,在python3,名爲jsonrpclib-pelix。

服務器端:jsonrpc_server.py

#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter18/jsonrpc_server.py
# JSON-RPC server needing "pip install jsonrpclib-pelix"

from jsonrpclib.SimpleJSONRPCServer import SimpleJSONRPCServer

def lengths(*args):
    """Measure the length of each input argument.

    Given N arguments, this function returns a list of N smaller
    lists of the form [len(arg), arg] that each state the length of
    an input argument and also echo back the argument itself.

    """
    results = []
    for arg in args:
        try:
            arglen = len(arg)
        except TypeError:
            arglen = None
        results.append((arglen, arg))
    return results

def main():
    server = SimpleJSONRPCServer(('localhost', 7002))
    server.register_function(lengths)
    print("Starting server")
    server.serve_forever()

if __name__ == '__main__':
    main()

客戶端:jsonrpc_client.py

#!/usr/bin/env python3
# Foundations of Python Network Programming, Third Edition
# https://github.com/brandon-rhodes/fopnp/blob/m/py3/chapter18/jsonrpc_client.py
# JSON-RPC client needing "pip install jsonrpclib-pelix"

from jsonrpclib import Server

def main():
    proxy = Server('http://localhost:7002')
    print(proxy.lengths((1,2,3), 27, {'Sirius': -1.46, 'Rigel': 0.12}))

if __name__ == '__main__':
    main()

​ wireshark的抓包結果:

{"version": "1.1",
"params": [[1, 2, 3], 27, {"Rigel": 0.12, "Sirius": -1.46}],
"method": "lengths"}
{"result": [[3, [1, 2, 3]], [null, 27],
[2, {"Rigel": 0.12, "Sirius": -1.46}]]

​ JSON-RPC還能夠給每一個請求增長id,這樣能夠同時發送多個請求。

相關文章
相關標籤/搜索