python 黏包现象
一、黏包
1、tcp有黏包现象
表现两种情况
发送的数据过小且下面还有一个发送数据,这两个数据会一起发送
发送的数据过大,超过最大缓存空间,超出的部分在下一次发送的时候发送
原因:
tcp是面向流的,根据算法,自动把数据拆分、组合,没有保护边界
2、udp无黏包现象
表现形式
发送的数据包大小超出最大缓存空间,超出的数据直接丢弃
udp不是面向流的,是面向消息的
总结
tcp协议是:可靠的,面向连接的,面向流的,效率低
udp协议是:不可靠的,无连接的,面向对象的,效率高
一般视频下载是tcp协议
聊天软件是udp协议
数据传输,传输的是数据包,数据包的内容是报文,报文有报头等
二、黏包现象
1、接连发生数据较小的数据包,且只接收数据一次
"""
Server端
在Client端接连发送两个小的数据包,Server端只有一个接收,且接收文件较大
会出现黏包现象
"""
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8010))
sk.listen()
connect, addr = sk.accept()
ret = connect.recv(1024)
print(ret.decode('utf-8'))
connect.close()
sk.close()
"""
Client端
"""
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8010))
sk.send('tom'.encode('utf-8'))
sk.send(' is god'.encode('utf-8'))
sk.close()
2、发送一个大的数据包,接收多次,且第一次接收的数据比较小
"""
Server端
在Client端发送一个数据包,Server端只接收两次,且第一次接受的数据较少
会出现黏包现象
"""
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8010))
sk.listen()
connect, addr = sk.accept()
ret1 = connect.recv(4).decode('utf-8')
ret2 = connect.recv(10).decode('utf-8')
print(ret1)
print(ret2)
connect.close()
sk.close()
"""
Client端
"""
import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8010))
sk.send('tom is god'.encode('utf-8'))
sk.close()
示例
"""
Server端
向Client端发送cmd命令,利用subprocess,执行命令并且发送两次
发送黏包现象
"""
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8010))
sk.listen()
connect, addr = sk.accept()
while 1:
cmd = input('>>>')
connect.send(cmd.encode('gbk'))
if cmd == 'q':
break
ret = connect.recv(1024).decode('gbk')
print(ret)
connect.close()
sk.close()
"""
Client端
"""
import socket
import subprocess
sk = socket.socket()
sk.connect(('127.0.0.1', 8010))
while 1:
cmd = sk.recv(1024).decode('gbk')
if cmd == 'q':
break
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
std_out = res.stdout.read() # bytes数据类型
std_error = res.stderr.read()
sk.send(std_out)
sk.send(std_error)
print(std_out.decode('gbk'))
print(std_error.decode('gbk'))
sk.close()
三、解决黏包
两种方法
1、预先知道发送端发送数据包的大小
2、使用struct变成固定大小的bytes类型
第一种方法,为了不产生黏包,每执行一次多产生一次网络延迟
"""
Server端
"""
import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8010))
sk.listen()
connect, addr = sk.accept()
while 1:
cmd = input('>>>')
connect.send(cmd.encode('utf-8'))
if cmd == 'q':
break
new_len = int(connect.recv(1024).decode('utf-8'))
connect.send(bytes('ok', 'utf-8'))
msg = connect.recv(new_len)
print(msg.decode('utf-8'))
connect.close()
sk.close()
"""
Client端
subprocess 产生的数据是bytes类型
计数bytes的长度->str
"""
import socket
import subprocess
sk = socket.socket()
sk.connect(('127.0.0.1', 8010))
while 1:
cmd = sk.recv(1024).decode('utf-8')
if cmd == 'q':
break
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
std_out = res.stdout.read()
std_err = res.stderr.read()
new_len = str(len(std_out + std_err))
sk.send(new_len.encode('utf-8'))
sk.recv(1024)
sk.send(std_out)
sk.send(std_err)
print(std_out.decode('utf-8'))
print(std_err.decode('utf-8'))
sk.close()
第二种方法使用struct
struct的应用
"""
'i'-> int
作用:把数字转换成固定4个字节的bytes类型
注意: unpack 时,要使用pack的返回值,unpack的是一个tuple,需要取第一个值
"""
import struct
a = struct.pack('i', 1234567)
print(a)
b = struct.unpack('i', a)[0]
print(b, type(b))
"""
b'\x87\xd6\x12\x00'
1234567 <class 'int'>
"""
解决黏包方法实现,每一次执行一次,对比上面的方法,少一次网络延迟
"""
Server端,接收pack的数据,unpack
"""
import socket
import struct
sk = socket.socket()
sk.bind(('127.0.0.1', 8010))
sk.listen()
connect, addr = sk.accept()
while 1:
cmd = input('>>>')
connect.send(cmd.encode('gbk'))
if cmd == 'q':
break
num = connect.recv(4)
b = struct.unpack('i', num)[0]
ret = connect.recv(b)
print(ret.decode('gbk'))
connect.close()
sk.close()
"""
Client端,将数据的长度pack,并传输
"""
import socket
import subprocess
import struct
sk = socket.socket()
sk.connect(('127.0.0.1', 8010))
while 1:
cmd = sk.recv(1024).decode('gbk')
if cmd == 'q':
break
res = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
std_out = res.stdout.read()
std_err = res.stderr.read()
new_len = len(std_out) + len(std_err)
res = struct.pack('i', new_len)
sk.send(res)
sk.send(std_out)
sk.send(std_err)
print(std_out.decode('gbk'))
print(std_err.decode('gbk'))
sk.close()
简单的文件下载
注意:文件的读写速度不一样,读的速度远大于写
"""
Server端
接收端
bytes->str->dict
"""
import socket
import struct
import json
buff = 1024
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sk.bind(('127.0.0.1', 8010))
sk.listen()
connect, addr = sk.accept()
pack_len = connect.recv(4)
head_len = struct.unpack('i', pack_len)[0]
head_bytes = connect.recv(head_len)
head_str = head_bytes.decode('utf-8')
head = json.loads(head_str)
print(head)
file_size = head['file_size']
with open(file=head['filename'], mode='wb') as f:
while file_size:
if file_size >= buff:
context = connect.recv(buff)
f.write(context)
file_size -= buff
print(file_size)
else:
try:
context = connect.recv(file_size)
f.write(context)
except TypeError:
print('integer argument expected, got float')
break
connect.close()
sk.close()
"""
Client端
发送端
dict->str->bytes
"""
import socket
import os
import json
import struct
buff = 1024
sk = socket.socket()
sk.connect(('127.0.0.1', 8010))
# 设置文件报头,dict
head = {'filepath': r'D:\Temp', 'filename': r'test.mp4', 'file_size': None}
file_path = os.path.join(head['filepath'], head['filename'])
file_size = os.path.getmtime(file_path)
head['file_size'] = file_size
# dict ->str
head_str = json.dumps(head)
# str -> bytes
head_bytes = head_str.encode('utf-8')
# 将长度,转换成固定长度的bytes类型
pack_len = struct.pack('i', len(head_bytes))
sk.send(pack_len)
sk.send(head_bytes)
print(file_path)
with open(file=file_path, mode='rb') as f:
while file_size:
if file_size >= buff:
context = f.read(buff)
sk.send(context)
file_size -= buff
print(file_size)
else:
try:
context = f.read()
sk.send(context)
except TypeError:
print('integer argument expected, got float')
break
sk.close()
python 黏包现象的更多相关文章
- python 黏包现象及其解决方案
一.数据缓冲区 缓冲区(buffer),它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的 ...
- python黏包现象
#黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...
- Python 之网络编程之socket(2)黏包现象和socketserver并发
一:黏包 ###tcp协议在发送数据时,会出现黏包现象. (1)数据粘包是因为在客户端/服务器端都会有一个数据缓冲区, 缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区 ...
- python中黏包现象
#黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...
- Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象
TCP的长连接 基于upd的socket服务 TCP黏包现象
- 黏包现象之TCP
老师的博客:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 server #_*_coding:gbk*_ from socket ...
- 铁乐学Python_Day34_Socket模块2和黏包现象
铁乐学Python_Day34_Socket模块2和黏包现象 套接字 套接字是计算机网络数据结构,它体现了C/S结构中"通信端点"的概念. 在任何类型的通信开始之前,网络应用程序必 ...
- day28 1.缓冲区 2.subprocess 3.黏包现象 4.黏包现象解决方案 5.struct
1.缓冲区: 输入缓冲区 输出缓冲区 2. subprocess的使用import subprocess sub_obj = subprocess.Popen('ls', #系统指令shell=Tr ...
- socket套接字模块及黏包现象
一.socket套接字模块 socket概念 socket层 理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模 ...
随机推荐
- javaScript通过URL获取参数
// 函数方法 function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=( ...
- vue 项目使用局域网多端访问并实时自动更新(利用 browser-sync)
在写vue的项目中时,虽然vue会自动更新页面,但是切换页面切来切去也很麻烦,有时候我们还要在公司另一台电脑或者手机上调试,这时候利用browser-sync插件,无需改动vue的代码即可实现: 1. ...
- Linux内核接口特定的类型
内核中一些通常使用的数据类型有它们自己的 typedef 语句, 因此阻止了任何移植性问 题. 例如, 一个进程标识符 ( pid ) 常常是 pid_t 而不是 int. 使用 pid_t 屏蔽了任 ...
- Linux 内核接口
USB 端点被绑在接口中. USB 接口只处理一类 USB 逻辑连接, 例如一个鼠标, 一个键盘, 或者一个音频流. 一些 USB 设备有多个接口, 例如一个 USB 扬声器可能有 2 个接口: 一个 ...
- Linux 内核USB 驱动
通用串行总线(USB)是一个在主机和许多外设之间的连接. 最初它被创建来替代许多慢速和不同的总线- 并口, 串口, 和键盘连接--有一个单个的所有设备都可以连接的总线类型.[45] USB 已经成长超 ...
- linux包之下载curl-wget
[root@localhost ~]# rpm -qa|grep curllibcurl-7.19.7-37.el6_4.x86_64python-pycurl-7.19.0-8.el6.x86_64 ...
- .net core允许跨域
// 设置允许所有来源跨域 app.UseCors(options => { options.AllowAnyHeader(); options.AllowAnyMethod(); option ...
- map实现删除给定字符串中的小写字母
def del_lowerletters(s): if s>='a' and s<='z': return " " else: return s print(" ...
- 定位布局中关于z-index的一些问题
定位布局中关于z-index的一些问题 使不同父元素的子元素不会被其他父元素遮盖 背景 两父元素相互遮盖(或部分遮盖) html如下 <div class="main"> ...
- Linux忘记root密码后如何在grub界面中以单用户模式进入系统并重置密码的方法
本文将介绍在Linux系统中忘记root用户密码的情况下,如何在gurb界面进入单用户模式并重置root用户密码.在单用户模式下,用户不需要输入任何密码即可进入系统并可以修改密码.实验步骤如下: 1. ...