socket--粘包
参考博客:http://www.cnblogs.com/kex1n/p/6502002.html
一、粘包现象
在上一篇的socket传输大数据文章中,我们可以顺利的接发数据,似乎做的不错,可以接收了。但是其实还隐藏着一个问题,请看下面的情况:

我们看到出错的位置,具体分析一下是什么情况

这个功能在上篇中介绍是用来判断数据量大小的,即服务端先将其要发送的数据量大小发给客户端,然后客户端再接收数据时,以此来判断是否接收了全部数据。
我们在来看看服务端的代码:
# 服务端先将数据大小发送给客户端,用于对比
conn.send(str(len('{}'.format(cmd_res).encode('utf-8'))).encode('utf-8'))
# 发送数据
conn.send('{}'.format(cmd_res).encode('utf-8'))
第一句代码的作用是发送数据量大小,第二句则是发送数据本身。由错误我们可以判断出,第一次发送的数据当中,不仅仅是数据量大小,即数据长度,还包含了第二次发送的部分数据。所以str格式的字符当然没办法 int了,就报错了。但是为什么发送的数据会混淆呢,why?
二、Socket粘包
同志们,这就是粘包了,也就是前后发送的数据在接收端那边是合在一起的。粘包现象不一定每次都出现,我试了二三十次才终于出现了。那么粘包现象是怎么来的呢?我目前知道的还少,只能有一点说一点了,先做个笔记,再慢慢了解了。
从现象上来看,粘包就是后一个数据紧跟着前一个数据的末尾被接收了,这样的影响就是如果两个包的数据格式不一样,分别要用来处理不同问题,比如上例中一个是int 一个是 str,就会出错。即便是数据格式一致,但前后数据连在一起,可能接收方就不知道其本意了。
2.1 考虑出现粘包
1)如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题。关闭连接主要是要双方都发送close连接,双方在数据发送和接收完毕后不再继续发送和接收新数据,而是双方关闭连接,这样就不用考虑粘包问题。
2)如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包
3)UDP发送数据不用考虑
4)如果双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构,需要考虑粘包
5)双方建立连接后,一段时间内不断发送数据,需要考虑粘包
2.2 粘包原因
- 发送端需要等缓冲区满才发送出去,造成粘包
- 接收方不及时接收缓冲区的包,造成多个包接收
具体点:
(1)发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。
(2)接收方引起的粘包是由于接收方用户进程不及时接收数据,从而导致粘包现象。这是因为接收方先把收到的数据放在系统接收缓冲区,用户进程从该缓冲区取数据,若下一包数据到达时前一包数据尚未被用户进程取走,则下一包数据放到系统接收缓冲区时就接到前一包数据之后,而用户进程根据预先设定的缓冲区大小从系统接收缓冲区取数据,这样就一次取到了多包数据。
粘包情况有两种,一种是粘在一起的包都是完整的数据包,另一种情况是粘在一起的包有不完整的包。
三、如何解决粘包
具体情况具体分析吧。我们还是以上例来处理
3.1 利用超时
socket将数据存入缓冲区后,TCP/IP协议会将数据发送给接收方,我们在两次连续发送数据时加入一个间隔时间,比如说:
conn.send(str(len('{}'.format(cmd_res).encode('utf-8'))).encode('utf-8'))
time.sleep(0.5) # 两次发送数据间隔一段时间
# 发送数据
conn.send('{}'.format(cmd_res).encode('utf-8', 'ignore'))
这样确实能解决问题,但是显然有缺陷,你让老司机发车的时候,总是停停顿顿的,他还不砸了硬盘?哦不,是方向盘。
3.2 接收端收到数据后回复一个确认
利用socket中的recv()函数是阻塞的,在接收端不返回确认时,就不会继续发送数据。同样在两次发送数据之间添加这个功能,代码如下:
服务端:
# -*- coding: UTF-8 -*-
import os
import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # TCP/IP协议, tcp ,如果不填写就是默认这个 server.bind(('localhost', 9999)) server.listen() while True: # 可以接受多个客户端 conn, addr = server.accept() while True: data = conn.recv(1024)
if not data: # 防止当接受的客户端数据为空时,程序卡掉
print('client has lost...')
break
print('执行命令:', data.decode())
cmd_res = os.popen(data.decode()).read() if len(cmd_res) == 0:
print('command not found')
else:
# 服务端先将数据大小发送给客户端,用于对比
conn.send(str(len('{}'.format(cmd_res).encode('utf-8'))).encode('utf-8'))
# 这里等待接收到返回
clientACK = conn.recv(1024)
# 发送数据
conn.send('{}'.format(cmd_res).encode('utf-8', 'ignore'))
print('发送完成')
客户端:
# -*- coding: UTF-8 -*-
import socket client = socket.socket() client.connect(('localhost', 9999)) while True:
cmd = input('>>:').strip()
# 判断是否发送空数据,如果是就重新发送
if len(cmd) == 0:
continue
else:
client.send(cmd.encode('utf-8'))
data_size = client.recv(1024) # 接收服务端发送的数据大小
print(data_size)
data_length = int(data_size.decode())
# 此处就是给服务端返回确认
client.send('返回确认收到数据大小,以解决粘包'.encode('utf-8')) print('返回数据大小:', data_length)
# 定义已接收数据大小为0
received_length = 0
# 定义已接收数据为0
received_data = b''
while received_length < data_length:
r_data = client.recv(1024) # 接受的数据是bytes类型
received_length += len(r_data)
received_data += r_data
else:
print('接收数据大小:', received_length)
print(received_data.decode('utf-8', 'ignore')) # 不加ignore在windows有时会报错
print('数据接收完毕!')
其实改的不是很多,就添加了两行代码,但是效果杠杠的,方向盘是保住了。
socket--粘包的更多相关文章
- Socket粘包问题
这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...
- C#下利用封包、拆包原理解决Socket粘包、半包问题(新手篇)
介于网络上充斥着大量的含糊其辞的Socket初级教程,扰乱着新手的学习方向,我来扼要的教一下新手应该怎么合理的处理Socket这个玩意儿. 一般来说,教你C#下Socket编程的老师,很少会教你如何解 ...
- [转]关于Socket粘包问题
这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题,发现自己不是很清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通 ...
- 解决Socket粘包问题——C#代码
解决Socket粘包问题——C#代码 前天晚上,曾经的一个同事问我socket发送消息如果太频繁接收方就会有消息重叠,因为当时在外面,没有多加思考 第一反应还以为是多线程导致的数据不同步导致的,让他加 ...
- Python socket粘包解决
socket粘包: socket 交互send时,连续处理多个send时会出现粘包,soket会把两条send作为一条send强制发送,会粘在一起. send发送会根据recv定义的数值发送一个固定的 ...
- TCP Socket 粘包
这两天看csdn有一些关于socket粘包,socket缓冲区设置的问题.发现自己不是非常清楚,所以查资料了解记录一下: 一两个简单概念长连接与短连接: 1.长连接 Client方与Server ...
- socket粘包现象加解决办法
socket粘包现象分析与解决方案 简单远程执行命令程序开发(内容回顾) res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=su ...
- 百万年薪python之路 -- socket粘包问题解决
socket粘包问题解决 1. 高大上版解决粘包方式(自定制包头) 整体的流程解释 整个流程的大致解释: 我们可以把报头做成字典,字典里包含将要发送的真实数据的描述信息(大小啊之类的),然后json序 ...
- Socket粘包问题终极解决方案—Netty版(2W字)!
上一篇我们讲了<Socket粘包问题的3种解决方案>,但没想到评论区竟然炸了.介于大家的热情讨论,以及不同的反馈意见,本文就来做一个扩展和延伸,试图找到问题的最优解,以及消息通讯的最优解决 ...
- socket 粘包问题(转)
https://www.v2ex.com/t/234785#reply3 1. 面向字节流的 IO 都有这个问题. socket 中 tcp 协议是面向流的协议,发送方发送和接收方的接收并不是一一对应 ...
随机推荐
- JavaScript 高级之面向对象
1. 对象属性及方法 创建对象的方式 <script> //创建对象的方式一 var obj = {}; //创建对象的方式一 var obj = new Object(); </s ...
- 第二次ScrumMeeting博客
第二次ScrumMeeting博客 本次会议于10月26日(四)22时整在3公寓725房间召开,持续15分钟. 与会人员:刘畅.方科栋.辛德泰.窦鑫泽.张安澜. 1. 每个人的工作(有Issue的内容 ...
- Java反编译插件
一.eclipse->help->Eclipse Marketplace 如下图:搜索JadClipse,install进行下载安装,
- loadrunner socket协议问题归纳(3)
摘要:通过实例讲解loadrunner中的socket协议性能测试的一种测试方法,如何不依赖loadrunner既定规则,自行控制收发数据包 关键词:Loadrunner,socket,自行控制,收发 ...
- php 通过curl上传图片
通过curl上传图片 PHP < 5.5: 使用 目前使用的php版本 7.1 测试无法使用 前面加@ 的方法上传文件 ,查找资料 使用 curl_setopt($ch,CURLOPT_SAFE ...
- linux下svn操作(专)
原文地址:https://www.cnblogs.com/clicli/p/5913330.html svn命令在linux下的使用SVN软件版本管理 1.将文件checkout到本地目录svn ch ...
- CF 1008B Turn the Rectangles(水题+贪心)
There are n rectangles in a row. You can either turn each rectangle by 90 degrees or leave it as it ...
- WCF传送大数据时的错误“ 超出最大字符串内容长度配额”
格式化程序尝试对消息反序列化时引发异常: 尝试对参数 http://tempuri.org/ 进行反序列化时出错: GetLzdtArticleResult.InnerException 消息是“反序 ...
- Beta冲刺第二周王者荣耀交流协会第四次会议
1.例会照片: 成员:王超,高远博,冉华,王磊,王玉玲,任思佳,袁玥全部到齐. master:王玉玲 2.时间跨度: 2017年11月20日 18:00 — 18:13,总计13分钟. 3.地点: 一 ...
- HDU 5234 Happy birthday 01背包
题目链接: hdu:http://acm.hdu.edu.cn/showproblem.php?pid=5234 bc:http://bestcoder.hdu.edu.cn/contests/con ...