路飞学城Python-Day24

12.粘包现象
客户端接收的信息指定了的字节,TCP协议没有丢失协议,只是只能接收指定的字节数,于是产生出了粘包现象
服务端接收命令只能接收1024字节,服务端执行命令结果以后传输给客户端,客户端再以1024个字节接收,但是如果结果超过1024个字节以后也不能再接收了,导致结果不可控了,没有接收的信息就会形成数据残留留到传输管道里,新的数据再发送的时候才会把老的数据发送过来,这样数据的传输会越来越不准确,这就是粘包的现象
粘包现象:多个包的数据粘到一起了,在管道里根本不区分数据,流式数据传输的特性,所有数据全部传到一起不做任何区分
服务端
import socket
import subprocess
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))
phone.listen(5)
print('starting')
while True:
conn, client_addr = phone.accept()
print(client_addr)
while True:
try:
# 收到命令
cmd = conn.recv(1024)
if not cmd:break
print('客户端接收的数据', cmd)
# 执行收到命令,拿到结果
f = subprocess.Popen(cmd.decode('gbk'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout = f.stdout.read()
stderr = f.stderr.read()
conn.send(stdout+stderr)
# 把命令结果返回
except ConnectionRefusedError:
break
conn.close()
phone.close()
客户端
import socket
 
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))
while True:
# 发命令
cmd = input('>>>').strip()
if not cmd: continue
phone.send(cmd.encode('utf-8'))
# 拿到命令结果并打印
data = phone.recv(1024)
print(data.decode('gbk'))
phone.close()

13.粘包底层原理分析
运行一个程序(软件)和什么硬件有关?
硬盘>>内存>>Cpu
send和recv的实现原理
站在客户端的角度讲,执行一次send的操作是应用程序的代码,想要将自己的数据发出去,但是应用程序不能执行直接发送,需要给自己的操作系统转交发送,但是客户端的内存和服务端的内存是相互隔离的,操作系统遵循tcp协议之后,通过接收到的数据发送,send只是拷贝给自己的操作系统的内存,send对于应用程序来说不过是完成了给内存的传输
数据接收,recv实际分为两个过程,1.数据接收,放到服务端的缓存中 2.从缓存中调取数据,解析发给应用程序
粘包发生的原因:数据量比较小,而且发送时间短
粘包是tcp的底层的优化算法决定的,但是解决粘包的关键就是需要指定发送文件的长度就可以了
服务端(粘包)
import socket
 
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('127.0.0.1', 8080))
server.listen(5)
conn, addr = server.accept()
data = conn.recv(1024)
print(data)
data1 = conn.recv(1024)
print(data1)
客户端
import socket
 
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
client.send('hello'.encode('utf-8'))
client.send('world'.encode('utf-8'))

14.解决粘包问题-伪代码实现
解决粘包问题的就是告诉发送方,需要发送的数据大小
服务端
import socket
import subprocess
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))
phone.listen(5)
print('starting')
while True:
conn, client_addr = phone.accept()
print(client_addr)
while True:
try:
# 收到命令
cmd = conn.recv(1024)
if not cmd:break
print('客户端接收的数据', cmd)
# 执行收到命令,拿到结果
f = subprocess.Popen(cmd.decode('gbk'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout = f.stdout.read()
stderr = f.stderr.read()
# 1.把报头(固定长度)数据的长度发送给客户端
total_size = len(stdout+stderr)
conn.send(str(total_size).encode('gbk'))
# 2.再发送真实的数据
 
conn.send(stdout)
conn.send(stderr)
# 把命令结果返回
except ConnectionRefusedError:
break
conn.close()
phone.close()
客户端
import socket
 
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))
while True:
# 发命令
cmd = input('>>>').strip()
if not cmd: continue
phone.send(cmd.encode('utf-8'))
# 第一步拿到数据长度
total_size = 1000000
# 第二步接收真实的数据
recv_size = 0
recv_data = b''
while recv_size<total_size:
res = phone.recv(1024)
recv_data += res
recv_size += len(res)
 
print(recv_data.decode('gbk'))
phone.close()

15.解决粘包问题-简单版本
使用struck模块打包数据报头
服务端
import socket
import subprocess
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))
phone.listen(5)
print('starting')
while True:
conn, client_addr = phone.accept()
print(client_addr)
while True:
try:
# 收到命令
cmd = conn.recv(1024)
if not cmd:break
print('客户端接收的数据', cmd)
# 执行收到命令,拿到结果
f = subprocess.Popen(cmd.decode('gbk'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout = f.stdout.read()
stderr = f.stderr.read()
# 1.把报头(固定长度)数据的长度发送给客户端
 
total_size = len(stdout+stderr)
header = struct.pack('i', total_size)
conn.send(header)
conn.send(str(total_size).encode('gbk'))
# 2.再发送真实的数据
 
conn.send(stdout)
conn.send(stderr)
# 把命令结果返回
except ConnectionRefusedError:
break
conn.close()
phone.close()
客户端
import socket
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))
while True:
# 发命令
cmd = input('>>>').strip()
if not cmd: continue
phone.send(cmd.encode('utf-8'))
# 第一步拿到数据长度
total_size = struct.unpack('i', phone.recv(4))[0]
# 第二步接收真实的数据
recv_size = 0
recv_data = b''
while recv_size<total_size:
res = phone.recv(1024)
recv_data += res
recv_size += len(res)
print(recv_data.decode('gbk'))
phone.close()

16.解决粘包问题-终极版本
基于struck的打包会超过它的打包的长度,struck的模块有两种类型,i模式和l模式,对于大文件都有可能不够使用
import struct
res = struct.pack('i',12879999777)
print(res,type(res),len(res))
res1 = struct.unpack('i',res)
print(res1)
客户端
import socket
import struct
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.connect(('127.0.0.1', 8080))
while True:
# 发命令
cmd = input('>>>').strip()
if not cmd: continue
phone.send(cmd.encode('utf-8'))
# 第一步拿到数据长度
header_size = struct.unpack('i', phone.recv(4))[0]
# 接收报头内容
header_bytes = phone.recv(header_size)
# 从报头中解析真实数据的信息
header_json = header_bytes.decode('gbk')
header_dict = json.loads(header_json)
print(header_dict)
 
# 第二步接收真实的数据
total_size = header_dict['total_size']
recv_size = 0
recv_data = b''
while recv_size< total_size:
res = phone.recv(1024)
recv_data += res
recv_size += len(res)
print(recv_data.decode('gbk'))
phone.close()
服务端
import socket
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))
phone.listen(5)
print('starting')
while True:
conn, client_addr = phone.accept()
print(client_addr)
while True:
try:
# 收到命令
cmd = conn.recv(1024)
if not cmd:break
print('客户端接收的数据', cmd)
# 执行收到命令,拿到结果
f = subprocess.Popen(cmd.decode('gbk'), shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
stdout = f.stdout.read()
stderr = f.stderr.read()
# 1.制作固定长度的报头
header_dic = {
'filename': 'a.txt',
'md5':'xxxxxxdxxx',
'total_size': len(stdout)+len(stderr)
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('gbk')
# 发送报头的长度
conn.send(struct.pack('i',len(header_bytes)))
# 再发报头
conn.send(header_bytes)
# 2.再发送真实的数据
total_size = len(stdout + stderr)
header = struct.pack('i', total_size)
conn.send(header)
conn.send(str(total_size).encode('gbk'))
conn.send(stdout)
conn.send(stderr)
# 把命令结果返回
except ConnectionRefusedError:
break
conn.close()
phone.close()

17.文件传输功能实现

18.文件传输功能-函数版

19.文件传输功能-面向对象版

20.基于udp协议的套接字介绍
 

猜你喜欢

转载自www.cnblogs.com/pandaboy1123/p/9350605.html