在本节中,我们会用 Pyro4 学习如何写一个简单的客户端-服务器应用。这里提供的示例并不完整,但是可以成功运行。
一个客户端-服务器程序就是,在一个网络内,客户端连接上服务器来请求特定的服务,可以与其他的客户端共享软件/硬件资源,并使用相同的协议。在本节的系统中,server 管理一个在线购物网站,客户端负责已注册的用户连接到 server 来购物。
1. 如何做…
为了让例子尽量简单一些,本例只有三个脚本。第一个代表 client
对象,用来管理用户。第二个脚本是 shop
对象,第二个脚本是 server
。
Server 端的代码如下( server.py
):
"""The Shops server"""
from __future__ import print_function
import Pyro4
import shop
ns = Pyro4.naming.locateNS()
daemon = Pyro4.core.Daemon()
uri = daemon.register(shop.Shop())
ns.register("example.shop.Shop", uri)
print(list(ns.list(prefix="example.shop.").keys()))
daemon.requestLoop()
Client 端的代码如下 ( client.py
):
from __future__ import print_function
import sys
import Pyro4
# A Shop client.
class client(object):
def __init__(self, name, cash):
self.name = name
self.cash = cash
def doShopping_deposit_cash(self, Shop):
print("\n*** %s is doing shopping with %s:" % (self.name, Shop.name()))
print("Log on")
Shop.logOn(self.name)
print("Deposit money %s" % self.cash)
Shop.deposit(self.name, self.cash)
print("balance=%.2f" % Shop.balance(self.name))
print("Deposit money %s" % self.cash)
Shop.deposit(self.name, 50)
print("balance=%.2f" % Shop.balance(self.name))
print("Log out")
Shop.logOut(self.name)
def doShopping_buying_a_book(self, Shop):
print("\n*** %s is doing shopping with %s:" % (self.name, Shop.name()))
print("Log on")
Shop.logOn(self.name)
print("Deposit money %s" % self.cash)
Shop.deposit(self.name, self.cash)
print("balance=%.2f" % Shop.balance(self.name))
print("%s is buying a book for %s$" % (self.name, 37))
Shop.buy(self.name, 37)
print("Log out")
Shop.logOut(self.name)
if __name__ == "__main__":
ns = Pyro4.naming.locateNS()
uri = ns.lookup("example.shop.Shop")
print(uri)
Shop = Pyro4.core.Proxy(uri)
meeta = client("Meeta", 50)
rashmi = client("Rashmi", 100)
rashmi.doShopping_buying_a_book(Shop)
meeta.doShopping_deposit_cash(Shop)
print("")
print("")
print("")
print("")
print("The accounts in the %s:" % Shop.name())
accounts = Shop.allAccounts()
for name in accounts.keys():
print(" %s : %.2f" % (name, accounts[name]))
Shop 对象的代码如下 ( shop.py
):
class Account(object):
def __init__(self):
self._balance = 0.0
def pay(self, price):
self._balance -= price
def deposit(self, cash):
self._balance += cash
def balance(self):
return self._balance
class Shop(object):
def __init__(self):
self.accounts = {}
self.clients = ["Meeta", "Rashmi", "John", "Ken"]
def name(self):
return "BuyAnythingOnline"
def logOn(self, name):
if name in self.clients:
self.accounts[name] = Account()
else:
self.clients.append(name)
self.accounts[name] = Account()
def logOut(self, name):
print("logout %s" % name)
def deposit(self, name, amount):
try:
return self.accounts[name].deposit(amount)
except KeyError:
raise KeyError("unknown account")
def balance(self, name):
try:
return self.accounts[name].balance()
except KeyError:
raise KeyError("unknown account")
def allAccounts(self):
accs = {}
for name in self.accounts.keys():
accs[name] = self.accounts[name].balance()
return accs
def buy(self, name, price):
balance = self.accounts[name].balance()
self.accounts[name].pay(price)
下面开始执行这段代码,首先启动 Pyro4 的 name server:
C:>python -m Pyro4.naming
Not starting broadcast server for localhost.
NS running on localhost:9090 (127.0.0.1)
Warning: HMAC key not set. Anyone can connect to this server!
URI = PYRO:Pyro.NameServer@localhost:9090
然后,使用 python server.py
命令启动 Server. 命令行的显示如下图所示。
最后,用下面的命令启动客户端,模拟用户的动作。
python client.py
命令行的输出将如下所示:
C:\Users\Utente\Desktop\Python CookBook\Python Parallel Programming
INDEX\Chapter 5 - Distributed Python\
chapter 5 - codes\banks>python client.py
PYRO:obj_8c4a5b4ae7554c2c9feee5b0113902e0@localhost:59225
*** Rashmi is doing shopping with BuyAnythingOnline:
Log on
Deposit money 100
balance=100.00
Rashmi is buying a book for 37$
Log out
*** Meeta is doing shopping with BuyAnythingOnline:
Log on
Deposit money 50
balance=50.00
Deposit money 50
balance=100.00
Log out
The accounts in the BuyAnythingOnline:
Meeta : 100.00
Rashmi : 63.00
输出表示两个用户的会话, Meeta 和 Rashmi .
2. 讨论
客户端应用必须找到 Shop()
对象,通过如下的调用:
ns = Pyro4.naming.locateNS()
然后打开一个通讯的 channel:
daemon = Pyro4.core.Daemon()
uri = daemon.register(shop.Shop())
ns.register("example.shop.Shop", uri)
daemon.requestLoop()
shop.py 有处理账户和购物的脚本。 shop
类将会管理每一个账户,它提供了登陆登出,管理用户的余额,以及处理购买动作:
class Shop(object):
def logOn(self, name):
if name in self.clients:
self.accounts[name] = Account()
else:
self.clients.append(name)
self.accounts[name] = Account()
def logOut(self, name):
print("logout %s" % name)
def deposit(self, name, amount):
try:
return self.accounts[name].deposit(amount)
except KeyError:
raise KeyError("unknown account")
def balance(self, name):
try:
return self.accounts[name].balance()
except KeyError:
raise KeyError("unknown account")
def buy(self, name, price):
balance = self.accounts[name].balance()
self.accounts[name].pay(price)
每一个顾客都有自己的 Account
对象,提供存款管理。
class Account(object):
def __init__(self):
self._balance = 0.0
def pay(self, price):
self._balance -= price
def deposit(self, cash):
self._balance += cash
def balance(self):
return self._balance
最后, client.py
有一个启动模拟客户行为的类。在 main
函数中,我们模拟了两个用户, Rashmi 和 Meeta :
meeta = client('Meeta',50)
rashmi = client('Rashmi',100)
rashmi.doShopping_buying_a_book(Shop)
meeta.doShopping_deposit_cash(Shop)
他们在网站上存上一些现金,然后开始购物:
# Rashmi 买了一本书:
def doShopping_buying_a_book(self, Shop):
Shop.logOn(self.name)
Shop.deposit(self.name, self.cash)
Shop.buy(self.name,37)
Shop.logOut(self.name)
# Meeta 分两次充了 $100 到账户中:
def doShopping_deposit_cash(self, Shop):
Shop.logOn(self.name)
Shop.deposit(self.name, self.cash)
Shop.deposit(self.name, 50)
Shop.logOut(self.name)
最后程序打印出两个用户的存款:
print("The accounts in the %s:" % Shop.name())
accounts = Shop.allAccounts()
for name in accounts.keys():
print(" %s : %.2f" % (name, accounts[name]))
下一节:PyCSP 是基于通信顺序进程的一个 Python 模块。通信顺序进程是通过消息处理建立并发程序的编程范式。