一、黏包

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. C# double 好用的扩展

    在很多代码需要使用数学计算,在用到 double 很难直接判断一个值是 0 或者 1 ,判断两个值相等. 本文提供一个数学扩展,让大家可以简单使用到 double 判断 在开始看本文之前,希望大家是知 ...

  2. 中和IOS七层架构和TCP/IP四层架构的五层架构

    五层架构分别为应用层.运输层.网络层.数据链路层.物理层. IOS架构把应用层又细分为应用层.表示层.会话层 TCP/IP把网络层改名网际层,数据链路层和物理层结合成网络接口层 其实只要学习五层协议, ...

  3. HDU 5912 Fraction(模拟)

    Problem Description Mr. Frog recently studied how to add two fractions up, and he came up with an ev ...

  4. Linux 内核热插拔操作

    热插拔事件的实际控制是通过一套存储于 kset_hotplug_ops 结构的方法完成. struct kset_hotplug_ops { int (*filter)(struct kset *ks ...

  5. dotnet core 集成到 Mattermost 聊天工具

    在找了很久的团队交流工具,发现了 Mattermost 最好用,但是还需要做一些定制化的功能,于是就找到了 Mattermost 插件开发,还找到了如何自己写服务集成到 Mattermost 里面 本 ...

  6. 纯CSS制作空心三角形和实心三角形及其实现原理

    纯CSS制作空心三角形和实心三角形及其实现原理 在一次项目中需要使用到空心三角形,我瞬间懵逼了.查阅了一些资料加上自己的分析思考,终于是达到了效果,个人感觉制作三角形是使用频率很高的,因此记录下来,供 ...

  7. dotnet 使用 Environment.FailFast 结束程序

    在运行到一些诡异的代码,这时的程序已经无法继续运行,需要退出,那么如何在记完日志之后在退出程序记录更多信息?可以通过 Environment.FailFast 里面添加字符串告诉用户当前的进程无法继续 ...

  8. dotnet 通过 WMI 获取设备厂商

    本文告诉大家如何通过 WMI 获取设备厂商 通过 Win32_ComputerSystem 可以获取电脑系统信息 通过下面代码可以获取 机器型号 和 制造厂商 var mc = "Win32 ...

  9. SPOJ VLATTICE (莫比乌斯反演)

    传送门:https://www.spoj.com/problems/VLATTICE/en/ 题意: 在三维坐标系下,你在点(0,0,0),看的范围是(n,n,n)以内,求你可以看见多少个点没有被遮挡 ...

  10. freemarker<三>

    前两篇博客介绍了freemaker是什么以及简单的语法规则,下面我们通过实现一个demo来看在实际应用中如何使用freemaker,本篇博客主要介绍freemaker与spring的整合. 需要的Ja ...