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.

Step 1: The Server Code (server.py)
This server will:
- Define a few functions (e.g.,
add,subtract,get_data). - Register these functions with the XML-RPC dispatcher.
- 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.

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:

--- 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
-
Threading and Concurrency The default
SimpleXMLRPCServerhandles one request at a time. If a client callslong_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,
SimpleXMLRPCServerruns in a multi-threaded mode. This means each incoming request is handled in a new thread, allowing the server to serve multiple clients concurrently. -
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
SimpleXMLRPCServerbehind 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
- Use HTTPS: Wrap your server with an SSL/TLS layer. This encrypts the traffic. You can do this by putting
