python - 通过 Python 套接字从客户端使用对象方法
问题描述
我的目标基本上是拥有我创建的这个“memcached”对象,它基本上是一个带有一组类似于 memchached 系统的命令的字典,并从客户端的角度操作它们,并实际对“memcached 字典对象”进行一些更改'。
首先,我决定制作一个 memcached.py 脚本,它基本上是一个 Memcached 类,其中包含一些真实 Memcached 系统的命令,但作为方法。
'''Class built with the intention of simulating a real Memcached system'''
class Memcached:
def __init__(self):
self.storage = {}
"""Storage Commands"""
def set(self, key, value):
self.storage[key] = value
print("STORED")
def add(self,key,value):
if key in self.storage:
print("ERROR")
else:
self.storage[key] = value
print("STORED")
"""Retrieval Commands"""
def get(self, key):
response = print(f'VALUE {key} \n {self.storage[key]} \n END')
return response
这显然可以从终端工作,所以这是我在 Memcached 系统的复杂性方面所能走的最远的一步(当我解决更大的问题时,我将添加一些其他功能/命令)
更大的问题:
我现在要做的实际上是通过 localhost 套接字发送和接收这个 memcached 对象,因此(例如)客户端可以从我创建的这个 memcached 对象中存储和检索信息。
这些基本上是我制作的脚本:
服务器.py
import socket
import json
import memcached
cache = memcached.Memcached()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', 12345))
s.listen(1)
print('Socket is listening...')
conn, addr = s.accept()
b = b''
with conn:
while True:
data = conn.recv(1024)
b += data
if not data:
break
d = json.loads(b.decode('utf-8'))
args = d.split(' ')
if args[0] == 'set':
cache.set(args[1], args[2])
elif args[0] == 'add':
cache.add(args[1], args[2])
elif args[0] == 'get':
cache.get(args[1], args[2])
else:
print('ERROR')
#control print
print(d)
客户端.py
import socket
import json
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('localhost', 12345))
class Client:
def __init__(self):
self.host = 'localhost'
self.port = 12345
def command(self, command, key, value):
cmd = command + ' ' + key + ' ' + value
b = json.dumps(cmd).encode('utf-8')
s.sendall(b)
newClient = Client()
while True:
print('Taking memcached commands...')
cmd = input()
if cmd == 'quit':
break
else:
cmdList = cmd.split()
newClient.command(cmdList[0], cmdList[1], cmdList[2])
已编辑:使用允许我从客户端加入 memcached 方法的逻辑更新了脚本。但是现在我无法从同一个连接发送不同的命令。线程是必要的还是逻辑上的简单改变可以解决这个问题?例如,在我所处的当前状态下,我不能使用“get”命令,因为字典基本上是在客户端脚本结束后重建的。
解决方案
您必须运行代码while True:
才能为下一个命令重复它。
如果你想连接很多次,那么你必须在另一个中运行它while True
。
我没有,所以我用, ,memcache
创建了自己的类来模拟它。set
add
get
我'\n'
用来识别命令结束。
我不使用JSON
,split
但shlex.split
要正确拆分set X "hello world"
为['set', 'X', 'hello world']
服务器.py
import socket
import shlex
#import memcached
# --- classes ---
class MyMemcached():
def __init__(self):
self.data = dict()
def set(self, key, val):
print('[MyClass]: set', key, val)
self.data[key] = val
def add(self, key, val):
print('[MyClass]: add', key, val)
if key not in self.data:
self.data[key] = ""
self.data[key] += val
def get(self, key):
val = self.data.get(key)
print('[MyClass]: get', key, val)
return val
# --- main ---
#cache = memcached.Memcached()
cache = MyMemcached()
s = socket.socket() # default values are `socket.AF_INET, socket.SOCK_STREAM`
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # solution for: `OSError: [Errno 98] Address already in use` when socket already closed
print('Bind: 0.0.0.0:12345')
s.bind(('0.0.0.0', 12345))
print('Listen: 1')
s.listen(1) # how many clients can wait for connection
try:
while True: # repeat for next connection
print('Waiting for client...')
conn, addr = s.accept()
print('Connected:', addr)#, conn)
with conn:
while True: # repeat for next command
cmd = b''
# get one command
while True:
data = conn.recv(1) # read single char until you get `\n'
# read until `\n'`
if not data or data == b'\n':
break
cmd += data
cmd = cmd.decode('utf-8')
#control print
print('cmd:', cmd)
if cmd.lower() == 'quit':
break
#args = cmd.split(' ') # it split INCORRECTLY `set a "hello world"`
args = shlex.split(cmd) # it split correctly `set a "hello world"`
print('args:', args)
if args[0] == 'set':
cache.set(args[1], args[2])
elif args[0] == 'add':
cache.add(args[1], args[2])
elif args[0] == 'get':
val = cache.get(args[1])
print(val)
else:
print('ERROR: cmd:', cmd)
break
except KeyboardInterrupt:
print('Pressed Ctrl+C')
finally:
s.close()
客户端.py
import socket
# --- classes ---
class Client:
def __init__(self, host='0.0.0.0', port=8000):
self.host = host
self.port = port
self.socket = socket.socket() # default values are `socket.AF_INET, socket.SOCK_STREAM`
self.socket.connect((self.host, self.port))
def command(self, cmd):
print('send:', cmd)
cmd = cmd.encode('utf-8') + b'\n'
self.socket.sendall(cmd)
def run(self):
while True:
cmd = input('cmd: ')
self.command(cmd)
if cmd.lower() == 'quit':
break
# --- functions ---
# ... empty ...
# --- main ---
#client = Client('localhost', 12345)
client = Client(port=12345)
client.run()