浅谈tcp粘包问题
第一部分:简介tcp socket通信的底层原理
原理解析图:

socket通信过程如图所示:首先客户端将发送内容通过send()方法将内容发送到客户端计算机的内核区,然后由操作系统将内容通过底层路径发送到服务器端的内核区,然后由服务器程序通过recv()方法从服务器端计算机内核区取出数据。
因此我们可以了解到,send方法并不是直接将内容发送到服务器端,recv方法也并不是直接将从客户端发来的内容接收到服务器程序内存中,而是操作自己机器的内核区。
第二部分:产生粘包的原因(只针对tcp)
产生粘包的情况有两种:
1:当连续发送数据时,由于tcp协议的nagle算法,会将较小的内容拼接成大的内容,一次性发送到服务器端,因此造成粘包 2:当发送内容较大时,由于服务器端的recv(buffer_size)方法中的buffer_size较小,不能一次性完全接收全部内容,因此在下一次请求到达时,接收的内容依然是上一次没有完全接收完的内容,因此造成粘包现象。
也就是说:接收方不知道该接收多大的数据才算接收完毕,造成粘包。
第三部分:如何解决上述两种粘包现象?
思路一:对于第一种粘包产生方式可以在两次send()直接使用recv()来阻止连续发送的情况发生。代码就不用展示了。
思路二:由于产生粘包的原因是接收方的无边界接收,因此发送端可以在发送数据之前向接收端告知发送内容的大小即可。代码示例如下:
方式一:分两次通讯分别传递内容大小和内容
服务器端代码:
# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *
import subprocess server = socket(AF_INET, SOCK_STREAM)
server.bind(("127.0.0.1", 8000))
server.listen(5) while True:
conn, addr = server.accept()
print("创建了一个新的连接!")
while True:
try:
data = conn.recv(1024)
if not data: break
res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
err = res.stderr.read()
if err:
cmd_msg = err
else:
cmd_msg = res.stdout.read()
if not cmd_msg: cmd_msg = "action success!".encode("gbk")
length = len(cmd_msg)
conn.send(str(length).encode("utf-8"))
conn.recv(1024)
conn.send(cmd_msg)
except Exception as e:
print(e)
break
客户端代码:
# __author__:Kelvin
# date:2019/4/28 21:36
from socket import * client = socket(AF_INET, SOCK_STREAM)
client.connect(("127.0.0.1", 8000))
while True:
inp = input(">>:")
if not inp: continue
if inp == "quit": break
client.send(inp.encode("utf-8"))
length = int(client.recv(1024).decode("utf-8"))
client.send("ready!".encode("utf-8"))
lengthed = 0
cmd_msg = b""
while lengthed < length:
cmd_msg += client.recv(1024)
lengthed = len(cmd_msg)
print(cmd_msg.decode("gbk"))
方式二:一次通讯直接传递内容大小和内容
服务器端:
# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *
import subprocess
import struct server = socket(AF_INET, SOCK_STREAM)
server.bind(("127.0.0.1", 8000))
server.listen(5) while True:
conn, addr = server.accept()
print("创建了一个新的连接!")
while True:
try:
data = conn.recv(1024)
if not data: break
res = subprocess.Popen(data.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
stderr=subprocess.PIPE)
err = res.stderr.read()
if err:
cmd_msg = err
else:
cmd_msg = res.stdout.read()
if not cmd_msg: cmd_msg = "action success!".encode("gbk")
length = len(cmd_msg)
conn.send(struct.pack("i", length))
conn.send(cmd_msg)
except Exception as e:
print(e)
break
客户端:
# __author__:Kelvin
# date:2019/4/28 21:36
from socket import *
import struct client = socket(AF_INET, SOCK_STREAM)
client.connect(("127.0.0.1", 8000))
while True:
inp = input(">>:")
if not inp: continue
if inp == "quit": break
client.send(inp.encode("utf-8"))
length = struct.unpack("i",client.recv(4))[0]
lengthed = 0
cmd_msg = b""
while lengthed < length:
cmd_msg += client.recv(1024)
lengthed = len(cmd_msg)
print(cmd_msg.decode("gbk"))
上述两种方式均可以解决粘包问题。
浅谈tcp粘包问题的更多相关文章
- 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案
引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...
- Socket编程(4)TCP粘包问题及解决方案
① TCP是个流协议,它存在粘包问题 TCP是一个基于字节流的传输服务,"流"意味着TCP所传输的数据是没有边界的.这不同于UDP提供基于消息的传输服务,其传输的数据是有边界的.T ...
- Netty(三)TCP粘包拆包处理
tcp是一个“流”的协议,一个完整的包可能会被TCP拆分成多个包进行发送,也可能把小的封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题. 粘包.拆包问题说明 假设客户端分别发送数据包D1和D ...
- netty 解决TCP粘包与拆包问题(二)
TCP以流的方式进行数据传输,上层应用协议为了对消息的区分,采用了以下几种方法. 1.消息固定长度 2.第一篇讲的回车换行符形式 3.以特殊字符作为消息结束符的形式 4.通过消息头中定义长度字段来标识 ...
- Netty的TCP粘包/拆包(源码二)
假设客户端分别发送了两个数据包D1和D2给服务器,由于服务器端一次读取到的字节数是不确定的,所以可能发生四种情况: 1.服务端分两次读取到了两个独立的数据包,分别是D1和D2,没有粘包和拆包. 2.服 ...
- netty 解决TCP粘包与拆包问题(一)
1.什么是TCP粘包与拆包 首先TCP是一个"流"协议,犹如河中水一样连成一片,没有严格的分界线.当我们在发送数据的时候就会出现多发送与少发送问题,也就是TCP粘包与拆包.得不到我 ...
- TCP粘包/拆包问题
无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想想河 ...
- tcp粘包问题(封包)
tcp粘包分析 http://blog.csdn.net/zhangxinrun/article/details/6721495 解决TCP网络传输“粘包”问题(经典) http: ...
- 1. Netty解决Tcp粘包拆包
一. TCP粘包问题 实际发送的消息, 可能会被TCP拆分成很多数据包发送, 也可能把很多消息组合成一个数据包发送 粘包拆包发生的原因 (1) 应用程序一次写的字节大小超过socket发送缓冲区大小 ...
随机推荐
- Storyboard中ViewController加载的四种方式
这个总结来自于<Programming iOS 10>一书: 1.storyboard的初始化ViewController,通过方法instantiateInitialViewContro ...
- 使用Navicat连接阿里云服务器上的MySQL数据库--转
手把手教你如何正确连接阿里云服务器上的数据库: 1.首先打开Navicat,文件>新建连接>MySQL连接,其他的如一图所示. 2.因为是连接服务器上的MySQL,所以我们使用SSH连接, ...
- jdk和tomcat环境配置
一.安装JDK和Tomcat 1,安装JDK:直接运行jdk-7-windows-i586.exe可执行程序,默认安装即可. 备注:路径可以其他盘符,不建议路径包含中文名及特殊符号. 2.安装Tomc ...
- 使用MongoDB存储集合的一些问题
这两天在工作中被Mongo集合存储给整得头大,当然也是我的认知太浅,所以下面我来分享下我所遇到的这个问题希望有大佬能给出更好的解决方案, 1.需求: 存储一个从前端接收未知数据类型的集合 例: 由于是 ...
- Android 资源文件命名与使用
[推荐]资源文件需带模块前缀 [推荐]layout 文件的命名方式 Activity 的 layout 以 module_activity 开头 Fragment 的 layout 以 module_ ...
- Django+xadmin打造在线教育平台(九)
目录 在线教育平台(一) 在线教育平台(二) 在线教育平台(三) 在线教育平台(四) 在线教育平台(五) 在线教育平台(六) 在线教育平台(七) 在线教育平台( ...
- BOM和DOM的区别
一.BOM和DOM之间的关系图 window对象是BOM的顶层(核心)对象,所有对象都是通过它延伸出来的,也可以称为window的子对象由于window是顶层对象,因此调用它的子对象时可以不显示的指明 ...
- Robot Framework自动化_Selenium2Library 关键字
Robot Framework自动化_Selenium2Library 关键字 培训老师:肖能尤 2016/06/12 课程目的 一.Robot framework Selenium2Library ...
- Centos6.5DRBD加载失败,系统更换yum源(国内163)
我安装的系统是centos6.5的,要在系统上安装DRBD镜像软件,安装完后,无法加载modprobe drbd. 需要更新kernel. 1,首先,先把yum源更换成国内的,不然无法更新kernel ...
- bugku login2 writeup 不使用vps的方法
0x00前言 这个题是sql注入与命令执行相结合的一个题,思路有两个: 一.:sql注入登录web系统,命令执行反弹公网IP监听端口(需要vps),此种方法详见链接:http://www.bugku. ...