[TOC]subprocess模块?作用:?1、可以通过代码执行操作系统的终端命令?2、并返回终端执行命令后的结果TCP协议特性:是一个流式协议,会将多次连续发送的数据量小、间隔时
目录
- subprocess模块
- TCP协议
- 黏包问题
- 上传大文件
- UDP
- 简易聊天室
- SocketServer
subprocess模块
? 作用:
? 1、可以通过代码执行操作系统的终端命令
? 2、并返回终端执行命令后的结果
用法:
import subprocess
cmd = inpu('CMD >>>')
obj = subprocess.Popen(
cmd.decode('utf8'),
shell=True,
stdout= subprocess.PIPE,
stderr= subprocess.PIPE
)
result = obj.stdout.read() + obj.stderr.read()
print(result.decode('gbk'))
TCP协议
特性:是一个流式协议,会将多次连续发送的数据量小、间隔时间短的数据一次性打包发送
黏包问题
? 原因:
? 1、无法预测需要接收的数据的长度
? 2、多次连续发送数据量小、并且时间间隔短的数据一次性打包发送。
解决黏包问题: 告诉对端数据的确切长度。
-struct模块:
? 必须先发送报头,再发送数据:
? 1、自定义报头,将数据长度等数据的描述信息制作成字典,
? 2、将字典json.dumps序列化并编码转为bytes,用于传输
? 3、先将报头的长度,用struct.pack格式化成固定长度(4字节)
? 4、发送:先发送报头长度,再发送报头,然后发送数据正文
? 接收:
? 1、先接收到4个字节的报头长度信息,unpack解压得到报头的长度值
? 2、根据报头长度值接收报头,解码并json.load反序列得到数据正文的描述信息(包含数据长度)
? 3、根据数据长度接收数据正文,
? 用法:
import json,struct
#假设通过客户端上传10G:10000000的文件:小视频.txt
#为避免粘包,必须自定制报头
header={'file_size':10000000,'file_name':'小视频.txt', 'md5':'8f6fbf8347faa4924a76856701edb0f3'} #10G数据,文件路径和md5值
#为了该报头能传送,需要序列化并且转为bytes
head_bytes=bytes(json.dumps(header),encoding='utf-8') #序列化并转成bytes,用于传输
#为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
head_len_bytes=struct.pack('i',len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度
#客户端开始发送
conn.send(head_len_bytes) #先发报头的长度,4个bytes
conn.send(head_bytes) #再发报头的字节格式
conn.sendall(文件内容) #然后发真实内容的字节格式
#服务端开始接收
head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
x=struct.unpack('i',head_len_bytes)[0] #提取报头的长度
head_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
header=json.loads(json.dumps(header)) #提取报头
#最后根据报头的内容提取真实的数据,比如
real_data_len=s.recv(header['file_size'])
s.recv(real_data_len)
上传大文件
以上传视频为例:
客户端:
import socket
import struct
import json
client = socket.socket()
client.connect(
('127.0.0.1', 9527)
)
with open(r'C:\day 027\5 上传大文件.mp4', 'rb')as f:
movie_bytes = f.read()
head_dic = {
'file_name': '5 上传大文件.mp4',
'file_size': len(movie_bytes)
}
json_head = json.dumps(head_dic)
bytes_head = json_head.encode('utf-8')
headers = struct.pack('i', len(bytes_head))
client.send(headers)
client.send(bytes_head)
init_data = 0
num = 1
with open(r'C:\day 027\5 上传大文件.mp4', 'rb')as f:
while init_data send_data = f.read(1024)
num += 1
client.send(send_data)
print(send_data, f'第{num}段数据发送完成')
init_data += len(send_data)
print('发送完毕!')
服务端:
import socket
import json
import struct
server = socket.socket()
server.bind(
('127.0.0.1', 9527)
)
server.listen(5)
while True:
conn, addr = server.accept()
try:
headers = conn.recv(4)
head_len = struct.unpack('i', headers)[0]
byte_head = conn.recv(head_len)
head_dic = json.loads(byte_head.decode('utf-8'))
print(head_dic)
file_name = head_dic.get('file_name')
file_size = head_dic.get('file_size')
init_data = 0
num = 1
with open(file_name, 'wb') as f:
while init_data data = conn.recv(1024)
print(f'第{num}段',data)
f.write(data)
init_data += len(data)
print(f'{file_name}接收完毕!')
except Exception as e:
print('error:', e)
break
conn.close()
UDP
? UDP是一种传输协议
? 1、不需要建立双向管道
? 2、不会黏包
? 3、客户端给服务端发送数据,不需要等服务端的回执
? 4、服务端只接收需要接收的数据,不管客户端传过来多少
服务端:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(
('127.0.0.1', 9527)
)
msg1 = server.recvfrom(1024)
msg2 = server.recvfrom(1024)
msg3 = server.recvfrom(1024)
print(msg1, msg2, msg3)
客户端:
import socket
client =socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('127.0.0.1', 9527)
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)
执行结果:(b‘hello!‘, (‘127.0.0.1‘, 57182)) (b‘hello!‘, (‘127.0.0.1‘, 57182)) (b‘hello!‘, (‘127.0.0.1‘, 57182))
简易聊天室
基于TCP协议,用socket套接字实现一个服务端对多个客户端的简易聊天室:
服务端:
import socket
server = socket.socket(type=socket.SOCK_DGRAM)
server.bind(
('127.0.0.1', 9527)
)
while True:
# 服务端接收客户端传过来的消息
msg, addr = server.recvfrom(1024) # (消息,客户端地址)
msg1, addr1 = server.recvfrom(1024) # (消息,客户端地址)
msg2, addr2 = server.recvfrom(1024) # (消息,客户端地址)
print(addr)
print(addr1)
print(addr2)
print(msg.decode('utf-8'))
print(msg1.decode('utf-8'))
print(msg2.decode('utf-8'))
# 服务端往客户端发送消息
send_msg = input('服务端发送消息:').encode('utf-8')
server.sendto(send_msg, addr)
server.sendto(send_msg, addr1)
server.sendto(send_msg, addr2)
客户端1:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('127.0.0.1', 9527)
while True:
send_msg = input('客户端1: ').encode('utf-8')
# 发送消息必须要加上对方地址
client.sendto(send_msg, server_ip_port)
# 能接收任何人的消息
msg = client.recv(1024)
print(msg.decode('utf-8'))
客户端2:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('127.0.0.1', 9527)
while True:
send_msg = input('客户端2: ').encode('utf-8')
# 发送消息必须要加上对方地址
client.sendto(send_msg, server_ip_port)
# 能接收任何人的消息
msg = client.recv(1024)
print(msg.decode('utf-8'))
客户端3:
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
server_ip_port = ('127.0.0.1', 9527)
while True:
send_msg = input('客户端3: ').encode('utf-8')
# 发送消息必须要加上对方地址
client.sendto(send_msg, server_ip_port)
# 能接收任何人的消息
msg = client.recv(1024)
print(msg.decode('utf-8'))
先启动服务端,再依次启动客户端,可开始模拟聊天室聊天
SocketServer
? 是python内置模块,可以用来简化socket套接字服务端的代码
import socket
class MyTcpServer(socketserver, BaseRequestHandler):
def handler(self):
data = self.request.recv(1024)
pirnt(data)
send_msg = input('服务端:').encode('utf8')
self.request.send(send_msg)