浅谈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发送缓冲区大小 ...
随机推荐
- Day4_闭包含数
闭包函数: 闭包函数是在作用域的前提下 闭包含数:定义在函数内部的函数,包含对外部作用域名字的引用,而不是对全局作用域名字的引用,那么该内部函数就称为闭包含数. eg: x=1 def f1(): x ...
- SSH密钥认证添加方法和一些实用配置
更改SSH端口号 用账号密码进入主机 sudo nano /etc/ssh/sshd-config 再其中添加Port 22等或改变该条 添加公钥到主机 cd ~ sudo mkdir .ssh 此处 ...
- Hibernate二级缓存简述及基于Spring4,Hibernate5,Ehcache3的二级缓存配置
Hibernate L2缓存 缓存的分类 L2缓存工作原理 放入二级缓存的数据 Ehcache 依赖 ehcache.xml 常用的memoryStoreEvictionPolicy(缓存算法) eh ...
- Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class android.sup
解决:找不到资源文件: 系统会根据分辨率来选择加载不同drawable下文件夹的资源,如果只在一个文件下放了资源文件,不同的分辨率设备的会报错.
- Asp.Net MVC 中JS通过ajaxfileupload上传图片获取身份证姓名、生日、家庭住址等详细信息
客户要求用身份证图片上传获取身份证的详细信息就下来研究了一下(现在的客户真的懒 身份证信息都懒得输入了哈哈...),经过慢慢研究,果然皇天不负有心人搞出来了.这个借助的是腾讯的一个SKD 腾讯优图云 ...
- Python_排版函数
import textwrap doc='''Beautiful is better than ugly. Explicit is better than implicit. Simple is be ...
- TCP连接和 time_wait、close_waite
TCP连接和 time_wait.close_waite tags:time_wait close_waite RST TCP 引言:前两天朋友公司的服务器垮掉了,最后查出的原因是发现大量的time_ ...
- QT5:C++实现基于Multimedia的音乐播放器(序)
前段时间C++课设,决定做个播放器,于是参考了网上的代码后,做了个很简陋的音乐播放器(只写了MP3格式)出来,虽然功能甚少,但还是决定把过程记录一下. 成品如下图: 播放器功能: 上.下一首,音量控制 ...
- Java 8 异常该进
try-with-resources 这个特性是在JDK7中出现的,我们在之前操作一个流对象的时候大概是这样的: try { // 使用流对象 stream.read(); stream.write( ...
- nginx cache的玩法
一.简介 Nginx版本从0.7.48开始,支持了类似Squid的缓存功能.这个缓存是把URL及相关组合当做Key,用Md5算法对Key进行哈希,得到硬盘上对应的哈希目录路径,从而将缓存内容保存在 ...