杰瑞科技汇

python xmlrpc server

Of course! Here is a comprehensive guide on creating a Python XML-RPC server, including a complete, runnable example, explanations of key concepts, and best practices.

What is XML-RPC?

XML-RPC (Remote Procedure Call) is a simple, lightweight protocol that uses XML to encode its calls and HTTP as a transport mechanism. It allows a program to make a function call on a remote computer, just as if it were a local function.

  • Client: Makes a request to a server.
  • Server: Listens for requests, executes the requested function, and returns the result (or an error).
  • Protocol: The request and response are formatted as XML and sent over HTTP.

Python has built-in support for XML-RPC in its standard library via the xmlrpc.server module, making it very easy to set up.


Part 1: A Simple XML-RPC Server

Let's create a basic server that exposes a few simple functions.

python xmlrpc server-图1

Step 1: The Server Code (server.py)

This server will:

  1. Define a few functions (e.g., add, subtract, get_data).
  2. Register these functions with the XML-RPC dispatcher.
  3. Start an HTTP server to listen for requests.
# server.py
import xmlrpc.server
import threading
import time
# --- Our functions to be exposed to the client ---
def add(x, y):
    """Adds two numbers and returns the result."""
    print(f"Server: Adding {x} and {y}")
    return x + y
def subtract(x, y):
    """Subtracts y from x and returns the result."""
    print(f"Server: Subtracting {y} from {x}")
    return x - y
def get_data():
    """Returns a dictionary with some data."""
    print("Server: Getting data dictionary")
    return {
        'name': 'XML-RPC Server',
        'version': '1.0',
        'status': 'running'
    }
def long_running_task(seconds):
    """Simulates a task that takes a long time."""
    print(f"Server: Starting a long task for {seconds} seconds...")
    time.sleep(seconds)
    print("Server: Long task finished.")
    return f"Task completed after {seconds} seconds."
# --- Server Setup ---
# Use '0.0.0.0' to listen on all available network interfaces,
# not just localhost. Use a port number > 1024 to avoid needing root privileges.
HOST = '0.0.0.0'
PORT = 8000
# Create the server instance
# The SimpleXMLRPCRequestHandler handles the XML-RPC protocol over HTTP.
# The allow_none=True argument allows Python's None to be used in the XML-RPC calls.
with xmlrpc.server.SimpleXMLRPCServer((HOST, PORT), allow_none=True) as server:
    # Register our functions with the server, giving them a public name
    server.register_function(add, 'add')
    server.register_function(subtract, 'subtract')
    server.register_function(get_data, 'get_data')
    server.register_function(long_running_task, 'long_running_task')
    # You can also register a single instance of a class
    # server.register_introspection_functions() # Allows the server to list its methods
    # server.register_multicall_functions() # Allows batch calls
    print(f"Server listening on http://{HOST}:{PORT}")
    print("Available functions: add, subtract, get_data, long_running_task")
    print("Use Ctrl+C to stop the server.")
    # Start serving requests
    server.serve_forever()

Step 2: Running the Server

Open your terminal, navigate to the directory where you saved server.py, and run it:

python server.py

You should see the following output, and the server will now be running and waiting for connections:

Server listening on http://0.0.0.0:8000
Available functions: add, subtract, get_data, long_running_task
Use Ctrl+C to stop the server.

Part 2: The XML-RPC Client

Now, let's create a client to interact with the server we just started.

python xmlrpc server-图2

Step 1: The Client Code (client.py)

This client will connect to the server and call the functions we registered.

# client.py
import xmlrpc.client
import time
# --- Client Setup ---
# The server's URL. It must be the full HTTP URL.
SERVER_URL = 'http://localhost:8000'
# Create a proxy object. This object represents the remote server.
# All function calls on this proxy will be sent to the server.
with xmlrpc.client.ServerProxy(SERVER_URL) as proxy:
    try:
        # --- Call the 'add' function ---
        print("--- Calling 'add' function ---")
        result_add = proxy.add(10, 5)
        print(f"Result from server: {result_add}\n")
        # --- Call the 'subtract' function ---
        print("--- Calling 'subtract' function ---")
        result_subtract = proxy.subtract(20, 8)
        print(f"Result from server: {result_subtract}\n")
        # --- Call the 'get_data' function ---
        print("--- Calling 'get_data' function ---")
        data = proxy.get_data()
        print(f"Result from server: {data}\n")
        # --- Call the 'long_running_task' function ---
        print("--- Calling 'long_running_task' function ---")
        # This will block the client until the server returns a result.
        print("Client: Waiting for server to complete long task...")
        start_time = time.time()
        result_long = proxy.long_running_task(3)
        end_time = time.time()
        print(f"Result from server: {result_long}")
        print(f"Client: Total time waited: {end_time - start_time:.2f} seconds\n")
        # --- Handling Errors ---
        print("--- Testing error handling ---")
        try:
            # This function doesn't exist on the server
            proxy.non_existent_function()
        except xmlrpc.client.Fault as err:
            print(f"Error: {err.faultCode} - {err.faultString}")
    except ConnectionRefusedError:
        print("Error: Could not connect to the server. Is it running?")
    except Exception as e:
        print(f"An unexpected error occurred: {e}")

Step 2: Running the Client

Make sure your server (server.py) is still running in another terminal. Now, run the client:

python client.py

Expected Output

You will see output from both the client and the server.

Client Terminal Output:

python xmlrpc server-图3

--- Calling 'add' function ---
Result from server: 15
--- Calling 'subtract' function ---
Result from server: 12
--- Calling 'get_data' function ---
Result from server: {'name': 'XML-RPC Server', 'version': '1.0', 'status': 'running'}
--- Calling 'long_running_task' function ---
Client: Waiting for server to complete long task...
Result from server: Task completed after 3 seconds
Client: Total time waited: 3.02 seconds
--- Testing error handling ---
Error: 1 - <class 'xmlrpc.client.Fault'>: <class 'exceptions.AttributeError'>: 'SimpleXMLRPCDispatcher' object has no attribute 'non_existent_function'

Server Terminal Output:

Server: Adding 10 and 5
Server: Subtracting 8 from 20
Server: Getting data dictionary
Server: Starting a long task for 3 seconds...
Server: Long task finished.

Key Concepts and Best Practices

  1. Threading and Concurrency The default SimpleXMLRPCServer handles one request at a time. If a client calls long_running_task, all other clients will have to wait. For real-world applications, you need a multi-threaded or multi-process server.

    Here's how to modify the server to use threads:

    # In server.py, replace the serve_forever() block with this:
    # Create a threaded server
    server = xmlrpc.server.SimpleXMLRPCServer((HOST, PORT), allow_none=True)
    # ... register functions as before ...
    print(f"Threaded server listening on http://{HOST}:{PORT}")
    server.serve_forever()

    By default, SimpleXMLRPCServer runs in a multi-threaded mode. This means each incoming request is handled in a new thread, allowing the server to serve multiple clients concurrently.

  2. Security (CRITICAL) The built-in XML-RPC server is not secure. It has no authentication, no encryption (everything is sent in plain text over HTTP), and is vulnerable to various attacks.

    • For Development Only: Use it only on a trusted local network or for testing.
    • For Production: Do not use the built-in server on the public internet.
    • Solutions:
      • Use HTTPS: Wrap your server with an SSL/TLS layer. This encrypts the traffic. You can do this by putting SimpleXMLRPCServer behind a proper web server like Nginx or Apache, which handles the SSL termination.
      • Add Authentication: You can write a custom request handler that checks for a username and password before passing

分享:
扫描分享到社交APP
上一篇
下一篇