听了林海峰老师讲的sockertserver的总结
一、首先在socketserver模块中分两大类:
server类(解决链接问题)和request类(解决通信问题)
1、server类
2、request类:
二、我将先为大家讲解基于TCP的sockertserver的基本使用
服务端代码:
import socketserver
class MyRequestHandle(socketserver.BaseRequestHandler):
def handle(self):
print(self.request) # 如果是tcp协议,self.request=>conn
print(self.client_address)
while True:
try:
msg=self.request.recv(1024)
if len(msg)==0:break
self.request.send(msg.upper())
except Exception:
break
self.request.close()
'''
s.serve_forever()=>等同于以下代码
while True:
conn,client_addr=server.accept()
启动一个线程(conn,client_addr)
'''
# 服务端应该做两件事
# 1、第一件事:循环的从半链接池中取出链接请求与其建立双向链接,拿到链接对象
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8888), MyRequestHandle)
s.serve_forever()
# 2、拿到链接对象,与其进行通信循环==>handle
客户端1代码:
from socket import *
import struct
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8888))
while True:
msg=input('请输入命令>>>>').strip()
if len(msg)==0:
continue
client.send((msg.encode('utf-8')))
recv_data=client.recv(1024)
print(recv_data.decode('utf-8'))
客户端2代码:
from socket import *
import struct
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8888))
while True:
msg=input('请输入命令>>>>').strip()
if len(msg)==0:
continue
client.send((msg.encode('utf-8')))
recv_data=client.recv(1024)
print(recv_data.decode('utf-8'))
运行结果:
# 服务端结果:
<socket.socket fd=812, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8888), raddr=('127.0.0.1', 63110)>
('127.0.0.1', 63110)
<socket.socket fd=692, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8888), raddr=('127.0.0.1', 63115)>
('127.0.0.1', 63115)
# 客户端1结果:
请输入命令>>>>asasa
ASASA
请输入命令>>>>ds
DS
请输入命令>>>>
# 客户端2结果:
请输入命令>>>>asas
ASAS
请输入命令>>>>
可以看出客户端1和客户端2是同时进行操作的,服务端同时接收到了两个消息
三、下面来为大家介绍基于UDP的sockertserver的并发思想
服务端代码:
import socketserver
class MyRequestHandle(socketserver.BaseRequestHandler):
def handle(self):
client_data=self.request[0]
server=self.request[1]
client_addr=self.client_address
print('客户端发来的数据%s'%client_data)
server.sendto(client_data.upper(),client_addr)
s=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyRequestHandle)
s.serve_forever()
# 相当于只负责循环的收
'''
s.serve_forever()=>等同于以下代码
while True:
data,client_addr=server.accept()
启动一个线程处理后续的事情(data,client_addr)
'''
客户端1代码:
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 数据报协议=》udp协议
while True:
msg=input('>>>>')
client.sendto(msg.encode('utf-8'),("127.0.0.1",8080))
data,server_addr=client.recvfrom(1024)
print(data.decode('utf-8'),server_addr)
client.close()
客户端2代码:
import socket
client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)# 数据报协议=》udp协议
while True:
msg=input('>>>>')
client.sendto(msg.encode('utf-8'),("127.0.0.1",8080))
data,server_addr=client.recvfrom(1024)
print(data.decode('utf-8'),server_addr)
client.close()
运行结果为:
# 服务端代码:
客户端发来的数据b'asa'
客户端发来的数据b'asasa'
客户端发来的数据b'asasasa'
# 客户端1代码:
>>>>asa
ASA ('127.0.0.1', 8080)
>>>>asasa
ASASA ('127.0.0.1', 8080)
>>>>
#客户端2代码:
>>>>asasasa
ASASASA ('127.0.0.1', 8080)
>>>>
四、用sockertserver基于TCP实现多客户远程执行命令
服务端:
import socketserver
import subprocess
import struct
import json
class MyRequestHandle(socketserver.BaseRequestHandler):
def handle(self):
print(self.request) # 如果是tcp协议,self.request=>conn
# print(self.client_address)
while True:
try:
cmd = self.request.recv(1024)
if len(cmd) == 0:
break
obj = subprocess.Popen(cmd.decode('utf-8'), # pycharm默认格式为utf-8
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout_res = obj.stdout.read()
stderr_res = obj.stderr.read()
total_size = len(stdout_res) + len(stderr_res)
header_dic = {
'total_size': total_size,
'client_address':self.client_address
}
print('客户端:%s 命令长度为:%d'%(header_dic['client_address'],header_dic['total_size']))
json_str = json.dumps(header_dic)
json_str_bytes = json_str.encode('utf-8')
# 2、先发头的长度
x = struct.pack('i', len(json_str_bytes)) # 将整形转换为bytes类型
self.request.send(x)
# 3、发头信息
self.request.send(json_str_bytes)
# 4再发真实的数据
self.request.send(stdout_res + stderr_res)
except Exception:
break
self.request.close()
'''
s.serve_forever()=>等同于以下代码
while True:
conn,client_addr=server.accept()
启动一个线程(conn,client_addr)
'''
# 服务端应该做两件事
# 1、第一件事:循环的从半链接池中取出链接请求与其建立双向链接,拿到链接对象
s = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), MyRequestHandle)
s.serve_forever()
# 2、拿到链接对象,与其进行通信循环==>handle
客户端1
from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('请输入命令>>>>').strip()
if len(msg)==0:
continue
client.send((msg.encode('utf-8')))
# 解决粘包问题:
# 一、先收固定长度的头:解析出数据的描述信息,包括拿数据的总大小total_size
# 1、先接收4个字节,从中提取接下来要收的头的长度
x = client.recv(4)
header = struct.unpack('i', x)[0]
# 2、接收头并解析
json_str_bytes = client.recv(header)
json_str = json_str_bytes.decode('utf-8')
header_dic =json.loads(json_str)
total_size = header_dic['total_size']
# 3、接收真实数据
recv_size=0
while recv_size<total_size:
recv_data=client.recv(1024)# 当收到的数据超过1024时,剩下的数据会继续在客户端的缓存中,新的数据也会在客户端的缓存中,等剩下的数据接收完了再接收新的数据
recv_size+=len(recv_data)
print(recv_data.decode('gbk'),end='')# windows默认是gbk格式,解码应为gbk格式
else:
print()
客户端2
from socket import *
import struct
import json
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8080))
while True:
msg=input('请输入命令>>>>').strip()
if len(msg)==0:
continue
client.send((msg.encode('utf-8')))
# 解决粘包问题:
# 一、先收固定长度的头:解析出数据的描述信息,包括拿数据的总大小total_size
# 1、先接收4个字节,从中提取接下来要收的头的长度
x = client.recv(4)
header = struct.unpack('i', x)[0]
# 2、接收头并解析
json_str_bytes = client.recv(header)
json_str = json_str_bytes.decode('utf-8')
header_dic =json.loads(json_str)
total_size = header_dic['total_size']
# 3、接收真实数据
recv_size=0
while recv_size<total_size:
recv_data=client.recv(1024)# 当收到的数据超过1024时,剩下的数据会继续在客户端的缓存中,新的数据也会在客户端的缓存中,等剩下的数据接收完了再接收新的数据
recv_size+=len(recv_data)
print(recv_data.decode('gbk'),end='')# windows默认是gbk格式,解码应为gbk格式
else:
print()
运行结果:
# 服务端结果:
<socket.socket fd=380, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 64118)>
客户端:('127.0.0.1', 64118) 命令长度为:20828
<socket.socket fd=752, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 64121)>
客户端:('127.0.0.1', 64121) 命令长度为:583
# 客户端1结果:
请输入命令>>>>tasklist
映像名称 PID 会话名 会话# 内存使用
========================= ======== ================ =========== ============
System Idle Process 0 Services 0 8 K
System 4 Services 0 84 K
Registry 124 Services 0 63,832 K
smss.exe 520 Services 0 1,036 K
csrss.exe 736 Services 0 5,152 K
wininit.exe 824 Services 0 5,752 K
csrss.exe 836 Console 1 6,856 K
services.exe 904 Services 0 9,748 K
lsass.exe 924 Services 0 24,384 K
svchost.exe 528 Services 0 33,208 K
fontdrvhost.exe 576 Services 0 2,452 K
svchost.exe 936 Services 0 16,768 K
svchost.exe 1044 Services 0 10,024 K
winlogon.exe 1212 Console 1 12,444 K
fontdrvhost.exe 1276 Console 1 10,464 K
dwm.exe 1356 Console 1 92,164 K
svchost.exe 1416 Services 0 10,700 K
svchost.exe 1516 Services 0 9,760 K
svchost.exe 1528 Services
。。。。。。。。。。。。。。。。。。。。。。。
conhost.exe 3528 Console 1 11,800 K
cmd.exe 8540 Console 1 5,092 K
tasklist.exe 15936 Console 1 9,652 K
请输入命令>>>>
# 客户端2
请输入命令>>>>dir
驱动器 D 中的卷是 所有文件软件丢到这里来
卷的序列号是 C0F9-5D0A
D:\新建文件夹\pythonProject1\shujialianxi\基于socketserver模板实现并发\基于TCP 的目录
2021/11/25 00:59 <DIR> .
2021/11/25 00:59 <DIR> ..
2021/11/25 00:01 <DIR> sockertserver基本使用
2021/11/24 11:46 0 __init__.py
2021/11/24 11:44 328 客户端.py
2021/11/24 11:44 328 客户端2.py
2021/11/25 00:59 2,171 服务端.py
4 个文件 2,827 字节
3 个目录 37,787,205,632 可用字节
请输入命令>>>>
版权声明:本文为m0_51734025原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。