《主机间通信》
因为项目需求,要实现一个能在程序运行过程中能与服务器做应答的功能,即传命令给服务器,并将运算结果回传。
Lib
库名:Deployment
Example
功能
- 客户机发消息给服务器
- 服务器回传图片给客户机
- 客户机展示图片
客户机
import json
import socket
import numpy as np
from PIL import Image
from Deployment.clientagency import MSession,poweroff
socket.setdefaulttimeout(10)
img2list=lambda img:np.array(img).tolist()
list2img=lambda img_list:Image.fromarray(np.array(img_list,dtype=np.uint8))
if __name__ == '__main__':
with MSession(("127.0.0.1",51888)) as conn:
conn.Msend("give me picture.",2**2)
jsonpacket=conn.Mrecv(2**2)
data=json.loads(jsonpacket)
img=list2img(data["img"])
img.show()
服务机
import json
import time
import numpy as np
from PIL import Image
from Deployment.serveragency import Server
img2list=lambda img:np.array(img).tolist()
list2img=lambda img_list:Image.fromarray(np.array(img_list,dtype=np.uint8))
def fuc(command):
print(command)
img=Image.open("test.jpeg")
data={"img":img2list(img)}
jsonpacket=json.dumps(data)
time.sleep(15)
return jsonpacket
if __name__ == '__main__':
with Server(("",51888),2**2) as worker:
worker.loading(fuc)
worker.run()
关闭服务器
from Deployment.clientagency import MSession,poweroff
if __name__ == '__main__':
with MSession(("127.0.0.1",51888)) as conn:
poweroff(conn,2**2)
Msocket
本类继承socket.socket
实现了Msend
&Mrecv
&Maccept
方法,能够在不丢包的要求下完成大文件传输。
socket.socket
里的sendall
&recv
一旦文件长了,就会发生严重丢包。
Mscoket
实现了分段发送和接受,网络不好的情况下,请刷低buffer size
。
#Msocket.py
import socket
import math
from socket import AddressFamily,SocketKind,getdefaulttimeout
class MMessage:
def __init__(self,message:str,block_size=2**10):
self.content=message.encode()
self.len_content=len(message)
self.block_size=block_size
def __iter__(self):
iter=0
len_send_data=0
len_content= self.len_content
yield len_content.to_bytes(self.block_size,'big',signed=False)
while len_content-len_send_data>=self.block_size:
message_slot=self.content[iter:iter+self.block_size]
iter+=len(message_slot)
len_send_data+=len(message_slot)
yield message_slot
if len_send_data!=len_content:
message=self.content[iter:]
iter+=len(message)
len_send_data+=len(message)
assert len_send_data==len_content
yield message
class MSocket(socket.socket):
def __init__(self, family: AddressFamily=-1, type: SocketKind=-1, proto: int=-1, fileno: int=None) -> None:
super().__init__(family=family, type=type, proto=proto, fileno=fileno)
def Msend(self,content,block_size=2**10):
for message_slot in MMessage(content,block_size):
self.sendall(message_slot)
def Mrecv(self,buffer_size=2**10):
len_slot=self.recv(buffer_size)
content_len=int.from_bytes(len_slot,byteorder='big',signed=False)
content_recv=b''
packet_num=math.ceil(content_len/buffer_size)
for _ in range(packet_num):
message_slot_recv=self.recv(buffer_size)
content_recv+=message_slot_recv
assert len(content_recv)==content_len
return content_recv.decode()
def Maccept(self):
fd, addr = self._accept()
sock = MSocket(self.family, self.type, self.proto, fileno=fd)
if getdefaulttimeout() is None and self.gettimeout():
sock.setblocking(True)
return sock, addr
clientagency
供客户机部署的类
from Deployment.Msocket import MSocket
import socket
def poweroff(agency,buffer_size):
agency.Msend('exit',buffer_size)
feedback=agency.Mrecv(buffer_size)
if feedback=='service stop.':
print("the remote host is shut down.")
class MSession:
def __init__(self,servername:tuple,selfname:tuple=None):
assert len(servername)==2
self.__agency=MSocket(socket.AF_INET,socket.SOCK_STREAM)
self.__servername=servername
if selfname is not None:
assert len(selfname)==2
self.__agency.bind(selfname)
def __enter__(self):
self.__agency.connect(self.__servername)
return self.__agency
def __exit__(self,*exc_info):
self.__agency.close()
@property
def name(self):
return self.__agency.getsockname()
serveragency
共服务器部署的类
from Deployment.Msocket import MSocket
import socket
class Server:
def __init__(self,selfname:tuple,buffer_size):
assert len(selfname)==2
agency=MSocket(socket.AF_INET,socket.SOCK_STREAM)
agency.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
agency.setsockopt(socket.SOL_SOCKET,socket.SO_KEEPALIVE,1)
agency.bind(selfname)
agency.listen(5)
self.__agency=agency
self.service=None
self.__buffer_size=buffer_size
def loading(self,function):
self.service=function
def run(self):
while True:
print("providing service ...")
client_socket,client_addr=self.agency.Maccept()
print("From host: ",client_addr)
command=client_socket.Mrecv(self.buffersize)
print("runing command",command)
if command=="exit":
print("server stop.")
client_socket.Msend("service stop.",self.buffersize)
self.agency.close()
break
else:
if self.service is None:
raise ValueError("server has no appointment yet.")
response=self.service(command)
client_socket.Msend(response,self.buffersize)
def __enter__(self):
return self
def __exit__(self,*exc_info):
self.agency.close()
@property
def agency(self):
return self.__agency
@property
def buffersize(self):
return self.__buffer_size