Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 45 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
45
Dung lượng
4,69 MB
Nội dung
CHAPTER 17 ■ FTP 304 C H A P T E R 18 ■ ■ ■ 305 RPC Remote Procedure Call (RPC) systems let you call a remote function using the same syntax that you would use when calling a routine in a local API or library. This tends to be useful in two situations: • Your program has a lot of work to do, and you want to spread it across several machines by making calls across the network. • You need data or information that is only available on another hard drive or network, and an RPC interface lets you easily send queries to another system to get back an answer. The first remote procedure systems tended to be written for low-level languages like C, and therefore placed bytes on the network that looked very much like the bytes already being written on to the processor stack every time one C function called another. And just as a C program could not safely call a library function without a header file that told it exactly how to lay out the function’s arguments in memory (any errors often resulted in a crash), RPC calls could not be made without knowing ahead of time how the data would be serialized. Each RPC payload, in fact, looked exactly like a block of binary data that has been formatted by the Python struct module that we looked at in Chapter 5. But today our machines and networks are fast enough that we are often in the mood to exchange some memory and speed for protocols that are more robust and that require less coordination between two pieces of code that are in conversation. Older RPC protocols would have sent a stream of bytes like the following: 0, 0, 0, 1, 64, 36, 0, 0, 0, 0, 0, 0 It would have been up to the receiver to know that the function’s parameters are a 32-bit integer and a 64-bit floating point number, and then to decode the twelve bytes to the integer 1 and the number 10.0. But these days the payload is likely to be XML, written in a way that makes it all but impossible to interpret the arguments as anything other than an integer and a floating-point number: <params> <param><value><i4>41</i4></value></param> <param><value><double>10.</double></value></param> </params> Our forefathers would be appalled that twelve bytes of actual binary data have bloated into 108 bytes of protocol that has to be generated by the sender and then parsed on the receiving end, consuming hundreds of CPU cycles. But the elimination of ambiguity in our protocols has generally been considered worth the expense. Of course, this pair of arguments can be expressed with less verbosity by using a more modern payload format like JSON: [1, 10.0] CHAPTER 18 ■ RPC 306 But in both cases you can see that unambiguous textual representation has become the order of the day, and it has replaced the older practice of sending raw binary data whose meaning had to be known in advance. Of course, you might be asking by this point exactly what makes RPC protocols at all special. After all, the choices we are talking about here — that you have to choose a data format, send a request, and receive a response in return — are not peculiar to procedure calls; they are common to any meaningful network protocol whatsoever! Both HTTP and SMTP, to take two examples from previous chapters, have to serialize data and define message formats. So again, you might wonder: what makes RPC at all special? There are three features that mark a protocol as an example of RPC. First, an RPC protocol is distinguished by lacking strong semantics for the meaning of each call. Whereas HTTP is used to retrieve documents, and SMTP supports the delivery of messages, an RPC protocol does not assign any meaning to the data passed except to support basic data types like integers, floats, strings, and lists. It is instead up to each particular API that you fashion using an RPC protocol to define what its calls mean. Second, RPC mechanisms are a way to invoke methods, but they do not define them. When you read the specification of a more single-purpose protocol like HTTP or SMTP, you will note that they define a finite number of basic operations — like GET and PUT in the case of HTTP, or EHLO and MAIL when you are using SMTP. But RPC mechanisms leave it up to you to define the verbs or function calls that your server will support; they do not limit them in advance. Third, when you use RPC, your client and server code should not look very different from any other code that uses function calls. Unless you know that an object represents a remote server, the only pattern you might notice in the code is a certain caution with respect to the objects that are passed — lots of numbers and strings and lists, but not live objects like open files. But while the kinds of arguments passed might be limited, the function calls will “look normal” and not require decoration or elaboration in order to pass over the network. Features of RPC Besides serving their the essential purpose of letting you make what appear to be local function or method calls that are in fact passing across the network to a different server, RPC protocols have several key features, and also some differences, that you should keep in mind when choosing and then deploying an RPC client or server. First, every RPC mechanism has limits on the kind of data you can pass. The most general-purpose RPC mechanisms tend to be the most restrictive because they are designed to work with many different programming languages and can only support lowest-common-denominator features that appear in almost all programming languages. The most popular protocols, therefore, support only a few kinds of numbers and strings; one sequence or list data type; and then something like a struct or associative array. Many Python programmers are disappointed to learn that only positional arguments are typically supported, since so few other languages at this point support keyword arguments. When an RPC mechanism is tied to a specific programming language, it is free to support a wider range of parameters; and in some cases, even live objects can be passed if the protocol can figure out some way to rebuild them on the remote side. In this case, only objects backed by live operating system resources — like an open file, live socket, or area of shared memory — become impossible to pass over the network. A second common feature is the ability of the server to signal that an exception occurred while it was running the remote function. In such cases, the client RPC library will typically raise an exception itself to tell the client that something has gone wrong. Of course, live traceback information of the sort that Python programmers are often fond of using typically cannot be passed back; each stack frame, for example, would try to refer to modules that do not actually exist in the client program. But at least some CHAPTER 18 ■ RPC 307 sort of proxy exception that gives the right error message must be raised on the client side of the RPC conversation when a call fails on the server. Third, many RPC mechanisms provide introspection, which is a way for clients to list the calls that are supported and perhaps to discover what arguments they take. Some heavyweight RPC protocols actually require the client and server to exchange large documents describing the library or API they support; others just let the list of function names and argument types be fetched by the client from the server; and other RPC implementations support no introspection at all. Python tends to be a bit weak in supporting introspection because Python, unlike a statically-typed language, does not know what argument types are intended by the programmer who has written each function. Fourth, each RPC mechanism needs to support some addressing scheme whereby you can reach out and connect to a particular remote API. Some such mechanisms are quite complicated, and they might even have the ability to automatically connect you to the correct server on your network for performing a particular task, without your having to know its name beforehand. Other mechanisms are quite simple and just ask you for the IP address, port number, or URL of the service you want to access. These mechanisms expose the underlying network addressing scheme, rather than creating a scheme of their own. Finally, some RPC mechanisms support authentication, access control, and even full impersonation of particular user accounts when RPC calls are made by several different client programs wielding different credentials. But features like these are not always available; and, in fact, simple and popular RPC mechanisms usually lack them entirely. Often these RPC schemes use an underlying protocol like HTTP that provides its own authentication, and they leave it up to you to configure whatever passwords, public keys, or firewall rules are necessary to secure the lower-level protocol if you want your RPC service protected from arbitrary access. XML-RPC We will begin our brief tour of RPC mechanisms by looking at the facilities built into Python for speaking XML-RPC. This might seem like a poor choice for our first example. After all, XML is famously clunky and verbose, and the popularity of XML-RPC in new services has been declining for years. But XML-RPC has native support in Python precisely because it was one of the first RPC protocols of the Internet age, operating natively over HTTP instead of insisting on its own on-the-wire protocol. This means our examples will not even require any third-party modules. While we will see that this makes our RPC server somewhat less capable than if we moved to a third-party library, this will also make the examples good ones for an initial foray into RPC. THE XML-RPC PROTOCOL Purpose: Remote procedure calls Standard: www.xmlrpc.com/spec Runs atop: HTTP Data types: int; float; unicode; list; dict with unicode keys; with non-standard extensions, datetime and None Libraries: xmlrpclib, SimpleXMLRPCServer, DocXMLRPCServer CHAPTER 18 ■ RPC 308 If you have ever used raw XML, then you are familiar with the fact that it lacks any data-type semantics; it cannot represent numbers, for example, but only elements that contain other elements, text strings, and text-string attributes. Thus the XML-RPC specification has to build additional semantics on top of the plain XML document format in order to specify how things like numbers should look when converted into marked-up text. The Python Standard Library makes it easy to write either an XML-RPC client or server, though more power is available when writing a client. For example, the client library supports HTTP basic authentication, while the server does not support this. Therefore, we will begin at the simple end, with the server. Listing 18–1 shows a basic server that starts a web server on port 7001 and listens for incoming Internet connections. Listing 18–1. An XML-RPC Server #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - xmlrpc_server.py # XML-RPC server import operator, math from SimpleXMLRPCServer import SimpleXMLRPCServer 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 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() An XML-RPC service lives at a single URL of a web site, so you do not have to actually dedicate an entire port to an RPC service like this; instead, you can integrate it with a normal web application that offers all sorts of other pages, or even entire other RPC services, at other URLs. But if you do have an entire port to spare, then the Python XML-RPC server offers an easy way to bring up a web server that does nothing but talk XML-RPC. You can see that the three sample functions that the server offers over XML-RPC — the ones that are added to the RPC service through the register_function() calls — are quite typical Python functions. And that, again, is the whole point of XML-RPC: it lets you make routines available for invocation over the network without having to write them any differently than if they were normal functions offered inside of your program. CHAPTER 18 ■ RPC 309 The SimpleXMLRPCServer offered by the Standard Library is, as its name implies, quite simple; it cannot offer other web pages, it does not understand any kind of HTTP authentication, and you cannot ask it to offer TLS security without subclassing it yourself and adding more code. But it will serve our purposes admirably, showing you some of the basic features and limits of RPC, while also letting you get up and running in only a few lines of code. Note that two additional configuration calls are made in addition to the three calls that register our functions. Each of them turns on an additional service that is optional, but often provided by XML-RPC servers: an introspection routine that a client can use to ask which RPC calls are supported by a given server; and the ability to support a multicall function that lets several individual function calls be bundled together into a single network round-trip. This server will need to be running before we can try any of the next three program listings, so bring up a command window and get it started: $ python xmlrpc_server.py Server ready The server is now waiting for connections on localhost port 7001. All of the normal addressing rules apply to this TCP server that you learned in Chapters 2 and 3, so you will have to connect to it from another command prompt on the same system. Begin by opening another command window and get ready to try out the next three listings as we review them. First, we will try out the introspection capability that we turned on in this particular server. Note that this ability is optional, and it may not be available on many other XML-RPC services that you use online or that you deploy yourself. Listing 18–2 shows how introspection happens from the client’s point of view. Listing 18–2. Asking an XML-RPC Server What Functions It Supports #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - xmlrpc_introspect.py # XML-RPC client import xmlrpclib proxy = xmlrpclib.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 '%s(%s)' % (method_name, signature) » else: » » print '%s( )' % (method_name,) » method_help = proxy.system.methodHelp(method_name) » if method_help: » » print ' ', method_help The introspection mechanism is an optional extension that is not actually defined in the XML-RPC specification itself. The client is able to call a series of special methods that all begin with the string system. to distinguish them from normal methods. These special methods give information about the other calls available. We start by calling listMethods(). If introspection is supported at all, then we will receive back a list of other method names; for this example listing, we ignore the system methods and only proceed to print out information about the other ones. For each method, we attempt to retrieve its CHAPTER 18 ■ RPC 310 signature to learn what arguments and data types it accepts. Because our server is Python-based, it does not actually know what data types the functions take: $ python xmlrpc_introspect.py Here are the functions supported by this server: concatenate( ) Add together everything in the list `things`. quadratic( ) Determine `x` values satisfying: `a` * x*x + `b` * x + c == 0 remote_repr( ) Return the `repr()` rendering of the supplied `arg`. However, you can see that, while parameter types are not given in this case, documentation strings are indeed provided — in fact, the SimpleXMLRPCServer has fetched our function’s docstrings and returned them for viewing by the RPC client. There are two uses that you might find for introspection in a real-world client. First, if you are writing a program that uses a particular XML-RPC service, then its online documentation might provide human-readable help to you. Second, if you are writing a client that is hitting a series of similar XML-RPC services that vary in the methods they provide, then a listMethods() call might help you work out which servers offer which commands. You will recall that the whole point of an RPC service is to make function calls in a target language look as natural as possible. And as you can see in Listing 18–3, the Standard Library’s xmlrpclib gives you a proxy object for making function calls against the server. These calls look exactly like local function calls. Listing 18–3. Making XML-RPC Calls #!/usr/bin/env python # -*- coding: utf-8 -*- # Foundations of Python Network Programming - Chapter 18 - xmlrpc_client.py # XML-RPC client import xmlrpclib proxy = xmlrpclib.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) Running the preceding code against our example server produces output from which we can learn several things about XML-RPC in particular, and RPC mechanisms in general. Note how almost all of the calls work without a hitch, and how both of the calls in this listing and the functions themselves back in Listing 18–1 look like completely normal Python; there is with nothing about them that is particular to a network: $ python xmlrpc_client.py xÿz 55 [0.0, 8.0] [-1.0] [1, 2.0, 'three'] [1, 2.0, 'three'] {'data': {'age': [42], 'sex': 'M'}, 'name': 'Arthur'} Download from Wow! eBook <www.wowebook.com> CHAPTER 18 ■ RPC 311 Traceback (most recent call last): xmlrpclib.Fault: <Fault 1: "<type 'exceptions.ValueError'>:math domain error"> The preceding snippet illustrates several key points about using XML-RPC. First, note that XML-RPC is not imposing any restrictions upon the argument types we are supplying. We can call addtogether() with either strings or numbers, and we can supply any number of arguments. The protocol itself does not care; it has no pre-conceived notion of how many arguments a function should take or what its types should be. Of course, if we were making calls to a language that did care — or even to a Python function that did not support variable-length argument lists — then the remote language could raise an exception. But that would be the language complaining, not the XML-RPC protocol itself. Second, note that XML-RPC function calls, like those of Python and many other languages in its lineage, can take several arguments, but can only return a single result value. That value might be a complex data structure, but it will be returned as a single result. And the protocol does not care whether that result has a consistent shape or size; the list returned by quadratic() (yes, I was tired of all of the simple add() and subtract() math functions that tend to get used in XML-RPC examples!) varies in its number of elements returned without any complaint from the network logic. Third, note that the rich variety of Python data types must be reduced to the smaller set that XML- RPC itself happens to support. In particular, XML-RPC only supports a single sequence type: the list. So when we supply remote_repr() with a tuple of three items, it is actually a list of three items that gets received at the server instead. This is a common feature of all RPC mechanisms when they are coupled with a particular language; types they do not directly support either have to be mapped to a different data structure (as our tuple was here turned into a list), or an exception has to be raised complaining that a particular argument type cannot be transmitted. Fourth, complex data structures in XML-RPC can be recursive; you are not restricted to arguments that have only one level of complex data type inside. Passing a dictionary with another dictionary as one of its values works just fine, as you can see. Finally, note that — as promised earlier — an exception in our function on the server made it successfully back across the network and was represented locally on the client by an xmlrpclib.Fault instance. This instance provided the remote exception name and the error message associated with it. Whatever the language used to implement the server routines, you can always expect XML-RPC exceptions to have this structure. The traceback is not terribly informative; while it tells us which call in our code triggered the exception, the innermost levels of the stack are simply the code of the xmlrpclib itself. Thus far we have covered the general features and restrictions of XML-RPC. If you consult the documentation for either the client or the server module in the Standard Library, you can learn about a few more features. In particular, you can learn how to use TLS and authentication by supplying more arguments to the ServerProxy class. But one feature is important enough to go ahead and cover here: the ability to make several calls in a network round-trip when the server supports it (it is another one of those optional extensions), as shown in Listing 18–4. Listing 18–4. Using XML-RPC Multicall #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - xmlrpc_multicall.py # XML-RPC client performing a multicall import xmlrpclib proxy = xmlrpclib.ServerProxy('http://127.0.0.1:7001') multicall = xmlrpclib.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 CHAPTER 18 ■ RPC 312 When you run this script, you can carefully watch the server’s command window to confirm that only a single HTTP request is made in order to answer all three function calls that get made: localhost - - [04/Oct/2010 00:16:19] "POST /RPC2 HTTP/1.0" 200 - The ability to log messages like the preceding one can be turned off, by the way; such logging is controlled by one of the options in SimpleXMLRPCServer. Note that the default URL used by both the server and client is the path /RPC2, unless you consult the documentation and configure the client and server differently. Three final points are worth mentioning before we move on to examining another RPC mechanism: • There are two additional data types that sometimes prove hard to live without, so many XML-RPC mechanisms support them: dates and the value that Python calls None (other languages call this null or nil instead). Python’s client and server both support options that will enable the transmission and reception of these non- standard types. • Keyword arguments are, alas, not supported by XML-RPC, because few languages are sophisticated enough to include them and XML-RPC wants to interoperate with those languages. Some services get around this by allowing a dictionary to be passed as a function’s final argument — or by disposing of positional arguments altogether and using a single dictionary argument for every function that supplies all of its parameters by name. • Finally, keep in mind that dictionaries can only be passed if all of their keys are strings, whether normal or Unicode. See the “Self-documenting Data” section later in this chapter for more information on how to think about this restriction. While the entire point of an RPC protocol like XML-RPC is to let you forget about the details of network transmission and focus on normal programming, you should see what your calls will look like on the wire at least once! Here is the first call to quadratic() that our sample client program makes: <?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> The response to the preceding call looks like this: <?xml version='1.0'?> <methodResponse> <params> <param> <value><array><data> <value><double>0.0</double></value> CHAPTER 18 ■ RPC 313 <value><double>8.0</double></value> </data></array></value> </param> </params> </methodResponse> If this response looks a bit verbose for the amount of data that it is transmitting, then you will be happy to learn about the RPC mechanism that we tackle next. JSON-RPC The bright idea behind JSON is to serialize data structures to strings that use the syntax of the JavaScript programming language. This means that JSON strings can be turned back into data in a web browser simply by using the eval() function. By using a syntax specifically designed for data rather than adapting a verbose document markup language like XML, this remote procedure call mechanism can make your data much more compact while simultaneously simplifying your parsers and library code. THE JSON-RPC PROTOCOL Purpose: Remote procedure calls Standard: http://json-rpc.org/wiki/specification Runs atop: HTTP Data types: int; float; unicode; list; dict with unicode keys; None Libraries: many third-party, including lovely.jsonrpc JSON-RPC is not supported in the Python Standard Library (at least at the time of writing), so you will have to choose one of the several third-party distributions available. You can find these distributions on the Python Package Index. My own favorite is lovely.jsonrpc, contributed to the Python community by Lovely Systems GmBH in Austria. If you install it in a virtual environment (see Chapter 1), then you can try out the server and client shown in Listings 18–5 and 18–6. Listing 18–5. A JSON-RPC Server #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - jsonrpc_server.py # JSON-RPC server from wsgiref.simple_server import make_server import lovely.jsonrpc.dispatcher, lovely.jsonrpc.wsgi def lengths(*args): » results = [] » for arg in args: » » try: » » » arglen = len(arg) » » except TypeError: » » » arglen = None » » results.append((arglen, arg)) » return results [...]... library, 66 Pylons, 189, 190 PyQt4 library, 164 Pyro, 317 Python community, 189 interface, 19 Wiki page, 189 Python data types and RPC systems, 311, 316 Python file objects from TCP streams, 49 Python Module of the Week”, 115 Python Package Index, 2 Python Standard Library, 2, 6 python- daemon, 100 python- lxml, 169 pyzeroconf, 70 ■Q qmail, 218, 221 QtWebKit, 164 Queue, 134 queues e-mail, 221 message queue... your computer! You can see an example client and server in Listings 18–7 and 18–8 If you want an example of the incredible kinds of things that a system like RPyC makes possible, you should study these listings closely Listing 18–7 An RPyC Client #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - rpyc_client.py # RPyC client import rpyc def noisy(string): » print 'Noisy:',... receive information through your own mechanism of choice, rather than insisting that HTTP to a fixed IP address be used 319 CHAPTER 18 ■ RPC Recovering From Network Errors Download from Wow! eBook Of course, there is one reality of life on the network that RPC services cannot easily hide: the network can be down or even go down in the middle of a particular RPC call You will find that... programming languages Instead, you should think of dictionaries being sent across RPCs as being like the dict attributes of your Python objects, which — if you are an experienced Python programmer — you should generally not find yourself using to associate an arbitrary set of keys with values! Just as your Python objects tend to have a small collection of attribute names that are well-known to your... network, and they become available for queries (You can also pass an object, and its methods will be registered with the server all at once.) Listing 18–6 JSON-RPC Client #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - jsonrpc_client.py # JSON-RPC client from lovely.jsonrpc import proxy proxy = proxy.ServerProxy('http://localhost:7002') print proxy.lengths((1,2,3), 27, {'Sirius':... model dictates that, absent any special permission, it will only allow clients to call methods that start with the special prefix, exposed_ Listing 18–8 An RPyC Server #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - rpyc_server.py # RPyC server import rpyc class MyService(rpyc.Service): » def exposed_line_counter(self, fileobj, function): » » for linenum, line in enumerate(fileobj.readlines()):... one item out of a highly connected object graph can be quickly transmitted, and only those parts of the graph that the remote site actually needs wind up getting transmitted However, both schemes often result in expensive and slow services, and they can make it very difficult to keep track of how one object is allowed to affect the answers provided by another service on the other end of the network In... killing multiprocessing, 120 kinit, 282 kqueue(), 113 ■L LARGER search criteria, 259 Last-modified: header, 155 latency, network, 103 –6 Launcelot protocol examples, 100 –120, 126 libcloud Python API, 264 libraries See also specific libraries advantages of, 8 need for, 2 web services programming and, 179 libwrap0, 90 libxml2, 169 lighttpd, 182 lighty, 182 line endings in IMAP, 261 Lines: header, 199 link... when all you have are Python programs that need to talk to each other, there is at least one excellent reason to look for an RPC service that knows about Python objects and their ways: Python has a number of very powerful data types, so it can simply be unreasonable to try “talking down” to the dialect of limited data formats like XML-RPC and JSON-RPC This is especially true when Python dictionaries,... 80 serv.inet, 100 server_loop(), 102 Server Name Indication (SNI), 156 servers architecture, 99–124 client/server pattern, 17 daemon programming, 99 directories and FTP, 299 event-driven, 109 –17 flup, 180, 183 frameworks, 114-116, 120, 179, 187–92, 319 inetd, 123 Launcelot example, 100 –120, 126 load balancing, 117, 129 multi-processing, 117–23 POP compatibility, 235 proxies, 117 pure -Python, 192 sharding . happens from the client’s point of view. Listing 18–2. Asking an XML-RPC Server What Functions It Supports #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - xmlrpc_introspect.py. incoming Internet connections. Listing 18–1. An XML-RPC Server #!/usr/bin/env python # Foundations of Python Network Programming - Chapter 18 - xmlrpc_server.py # XML-RPC server import operator,. function calls. Listing 18–3. Making XML-RPC Calls #!/usr/bin/env python # -*- coding: utf-8 -*- # Foundations of Python Network Programming - Chapter 18 - xmlrpc_client.py # XML-RPC client import