一、黏包

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 黏包现象的更多相关文章

  1. python 黏包现象及其解决方案

    一.数据缓冲区 缓冲区(buffer),它是内存空间的一部分.也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,显然缓冲区是具有一定大小的 ...

  2. python黏包现象

    #黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...

  3. Python 之网络编程之socket(2)黏包现象和socketserver并发

    一:黏包 ###tcp协议在发送数据时,会出现黏包现象.     (1)数据粘包是因为在客户端/服务器端都会有一个数据缓冲区,     缓冲区用来临时保存数据,为了保证能够完整的接收到数据,因此缓冲区 ...

  4. python中黏包现象

    #黏包:发送端发送数据,接收端不知道应如何去接收造成的一种数据混乱现象. #关于分包和黏包: #黏包:发送端发送两个字符串"hello"和"word",接收方却 ...

  5. Python网络编程基础 ❷ 基于upd的socket服务 TCP黏包现象

    TCP的长连接 基于upd的socket服务 TCP黏包现象

  6. 黏包现象之TCP

    老师的博客:http://www.cnblogs.com/Eva-J/articles/8244551.html#_label5 server #_*_coding:gbk*_ from socket ...

  7. 铁乐学Python_Day34_Socket模块2和黏包现象

    铁乐学Python_Day34_Socket模块2和黏包现象 套接字 套接字是计算机网络数据结构,它体现了C/S结构中"通信端点"的概念. 在任何类型的通信开始之前,网络应用程序必 ...

  8. day28 1.缓冲区 2.subprocess 3.黏包现象 4.黏包现象解决方案 5.struct

    1.缓冲区: 输入缓冲区  输出缓冲区 2. subprocess的使用import subprocess sub_obj = subprocess.Popen('ls', #系统指令shell=Tr ...

  9. socket套接字模块及黏包现象

    一.socket套接字模块 socket概念 socket层 理解socket Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口.在设计模式中,Socket其实就是一个门面模 ...

随机推荐

  1. Redis - 命令行工具

    使用Redis内置的命令行工具 redis-cli一些便捷的命令: 1.执行单条命令 平时在访问 Redis 服务器,一般都会使用 redis-cli 进入交互模式,然后一问一答来读写服务器,这种情况 ...

  2. H3C 根据主机地址数划分子网

  3. H3C调试信息输出的例子

  4. linux 禁止所有中断

    如果你需要禁止所有中断如何? 在 2.6 内核, 可能关闭在当前处理器上所有中断处理, 使用任一个下面 2 个函数(定义在 <asm/system.h>): void local_irq_ ...

  5. ASP.NET MVC4.0+EF+LINQ+bui+bootstrap+网站+角色权限管理系统(开篇)

    系统预览: 源码下载:http://www.yealuo.com/Home/Detail?KeyValue=2f926407-f80b-4bff-a729-949a53efed7b 创建项目,新手按步 ...

  6. Linux 内核子系统

    一个子系统是作为一个整体对内核一个高级部分的代表. 子系统常常(但是不是一直)出现 在 sysfs 层次的顶级. 一些内核中的例子子系统包括 block_subsys(/sys/block, 给块 设 ...

  7. C# 发送电子邮件(smtp)

    相关享目托管在github: https://github.com/devgis/CSharpCodes

  8. Mybatis 多对多(易百教程)

    mybatis3.0 添加了association和collection标签专门用于对多个相关实体类数据进行级联查询,但仍不支持多个相关实体类数据的级联保存和级联删除操作.因此在进行实体类多对多映射表 ...

  9. 微信群打卡机器人XiaoV项目开源 | 蔡培培的独立博客

    原文首发于蔡培培的独立博客.原文链接<微信群打卡机器人XiaoV项目开源>. 5月21日,在米花(后面" 亚里士多德式友谊"专题会提及)的影响下,决定搞个私人运动群,拉 ...

  10. 人生苦短,我用Python(4)

    1.创建数值元组: 在Python中,使用tuple()函数直接将range()函数循环出来的结果转换为数值元组. tuple(data) #tuple()函数的基本语法 data表示可以转换为元组的 ...