subprocess模块
1.可以帮你通过代码执行操作系统的终端命令
2.并返回终端执行命令后的结果
注意:stdout一定要在stderr前面
import subprocess
cmd=input('cmd>>>:')
obj=subprocess.Popen(
cmd,shell=True,
stdout=subprocess.PIPE,#返回正确结果参数
stderr=subprocess.PIPE#返回错误结果参数
)
result=obj.stdout.read()+obj.stderr.read()
print(result.decode('gbk'))
粘包问题
服务端第一次发送的数据,客户端无法精确一次性接受完毕。下一次发送的数据与上一次数据粘在一起了。
1.无法预测对方需要接受的数据大小长度
2.多次连续发送数据量小,并且时间间隔短的数据一次性打包发送
TCP协议特性:
tcp是一个流式协议,会将多次连续发送数据量小,并且时间间隔短的数据一次性打包发送
解决粘包问题
struct模块
是一个可以将很长的数据长度,压缩成固定的长度的一个标记(数据报头)。
必须先定义报头,发送报头,再发送真实数据
即想发送文件,又想发送文件的描述信息
'''
-struct模块:
是一个可以将很长的数据的长度,压缩成固定的长度的一个标记(数据报头)
'''
import struct
#打包压缩
# i模式,会将数据长度压缩成4个bytes
str1='nihaoyanihaoyanihaoya'
# 报头
headers=struct.pack('i', len(str1))
data_len=struct.unpack('i',headers)
print(data_len)#(21,)
print(data_len[0])#21
解决粘包问题
#服务端
import socket
import subprocess
import struct
server=socket.socket()
server.bind(('127.0.0.1',9527))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
cmd=conn.recv(1024).decode('utf-8')
if cmd=='q':
break
if len(cmd)==0:
continue
print(cmd)
# 执行cmd命令
obj=subprocess.Popen(cmd,shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE,)
#接受终端返回的数据
result=obj.stdout.read()+obj.stderr.read()
#打包压缩,获取报头
headers=struct.pack('i',len(result))
#发送报头
conn.send(headers)
#发送真实数据
conn.send(result)
except Exception as e:
print(e)
break
conn.close()
#客户端
import socket
import struct
client=socket.socket()
client.connect(('127.0.0.1',9527))
while True:
cmd=input('>>>:')
client.send(cmd.encode('utf-8'))
if cmd=='q':
break
#获取数据报头
headers=client.recv(4)
#解包,获取真实数据长度
data_len=struct.unpack('i',headers)
#获取真实数据
data=client.recv(data_len[0])
print(data.decode('gbk'))
client.close()
上传字典
#服务端
import socket
import json
import struct
server=socket.socket()
server.bind(('127.0.0.1',8888))
server.listen(5)
while True:
conn,addr=server.accept()
while True:
try:
headers=conn.recv(4)
data_len=struct.unpack('i',headers)[0]
bytes_data=conn.recv(data_len)
#先解码再反序列化
back_dic=json.loads(bytes_data.decode('utf-8'))
print(back_dic)
except Exception as e:
print(e)
break
conn.close()
#客户端
import socket
import struct
import json
import time
cilent=socket.socket()
cilent.connect(('127.0.0.1',8888))
while True:
send_dic={
'file_name':'nick真实写真集.avi',
'file_size':1000000
}
#json序列化,并转换成bytes类型数据
json_data=json.dumps(send_dic)
bytes_data=json_data.encode('utf-8')
# 先做报头
headers=struct.pack('i',len(bytes_data))
cilent.send(headers)
cilent.send(bytes_data)
time.sleep(3)
上传大文件
#服务端
import socket
import json
import struct
server=socket.socket()
server.bind(('127.0.0.1',8888))
server.listen(3)
while True:
conn,addr=server.accept()
while True:
try:
#获取字典报头
headers=conn.recv(4)
#解包获取字典数据长度
data_len=struct.unpack('i',headers)[0]
#获取字典真实数据
bytes_dic=conn.recv(data_len)
#樊=反序列得到字典
back_dic=json.loads(bytes_dic.decode('utf-8'))
print(back_dic)
#得到字典的文件名以及视频文件按的大小
file_name=back_dic['file_name']
file_size=back_dic['file_size']
init_data=0
#打开文件 准备写入
with open(file_name,'wb') as f:
while init_data<file_size:
data=conn.recv(1024)
f.write(data)
init_data+=len(data)
num=init_data/file_size
print(f'已接受{num:.2%}')
print(f'{file_name}接收完毕')
except Exception as e:
print(e)
break
conn.close()
#客户端
import socket
import struct
import json
client=socket.socket()
client.connect(('127.0.0.1',8888))
#打开一个视频文件,获取视频数据大小
with open(r'C:UserszqfDesktop新建文件夹八重樱_桃源恋歌_1080p.mp4','rb') as f:
movie_bytes=f.read()
# 为视频文件组织一个字典,字典内有视频文件按的名称和大小
send_dic={
'file_name':'八重樱-桃源恋歌',
'file_size':len(movie_bytes)
}
# 先打包字典 发送报头,再发送字典真实数据
json_data=json.dumps(send_dic)
bytes_data=json_data.encode('utf-8')
headers=struct.pack('i',len(bytes_data))
# 发送报头
client.send(headers)
#发送字典真实数据
client.send(bytes_data)
# 发送视频真实数据
init_data=0
with open(r'C:UserszqfDesktop新建文件夹八重樱_桃源恋歌_1080p.mp4','rb') as f:
while init_data<len(movie_bytes):
send_data=f.read(1024)
client.send(send_data)
num=init_data/len(movie_bytes)
print(f'数据传输{num:.2%}')
init_data+=len(send_data)
UDP
是一种传输协议
1.不需要建立双向管道
2.不会粘包
3.客户端给服务端发送数据,不需要等待服务端返回接受成功
4.数据容丢失,数据不安全
-TCP:好比打电话
-UDP:好比发短信
#服务端
import socket
#.SOCK_DGRAM代表了UDP
server=socket.socket(type=socket.SOCK_DGRAM)
#绑定了ip+port
server.bind(('127.0.0.1',8888))
msg,addr=server.recvfrom(1024)
msg1,addr1=server.recvfrom(1024)
msg2,addr2=server.recvfrom(1024)
print(msg,msg1,msg2)
#客户端
import socket
client=socket.socket(type=socket.SOCK_DGRAM)
server_ip_port=(('127.0.0.1',8888))
client.sendto(b'hello',server_ip_port)
client.sendto(b'hello',server_ip_port)
client.sendto(b'hello',server_ip_port)
client.sendto(b'hello',server_ip_port)
client.sendto(b'hello',server_ip_port)
QQ聊天室
#服务端
import socket
server=socket.socket(type=socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8888))
while True:
msg,addr=server.recvfrom(1024)
print(addr)
print(msg.decode('utf-8'))
send_msg=input("客户端向服务端发消息").encode('utf-8')
server.sendto(send_msg,addr)
#客户端
import socket
client=socket.socket(type=socket.SOCK_DGRAM)
server_ip_port=('127.0.0.1',8888)
while True:
send_msg=input('客户端向服务端发消息').encode('utf-8')
client.sendto(send_msg,server_ip_port)
msg=client.recv(1024)
print(msg.decode('utf-8'))
SocketServer
python内置模块,可以简化socket套接字服务端代码
-简化TCP/UDP服务代码
-必须要创建一个类
#服务端
import socketserver
#必须定义类
#TCP:必须继承BaseRequestHandler类
class MyTcpServer(socketserver.BaseRequestHandler):
def handle(self):
print(self.client_address)
while True:
try:
#request.recv==conn.recv
data=self.request.recv(1024).decode('utf-8')
send_msg=data.upper()
self.request.send(send_msg.encode('utf-8'))
except Exception as e:
print(e)
break
if __name__ == '__main__':
#socketserver.TCPServer只能一个服务
# server=socketserver.TCPServer(
# ('127.0.0.1',8888),MyTcpServer
# )
#
#socketserver.ThreadingTCPServer 多个服务
server=socketserver.ThreadingTCPServer(
('127.0.0.1', 8888), MyTcpServer
)
server.serve_forever()
#客户端
import socket
client=socket.socket()
client.connect(
('127.0.0.1',8888)
)
while True:
send_msg=input('客户端:')
client.send(send_msg.encode('utf-8'))
back_msg=client.recv(1024)
print(back_msg.decode('utf-8'))