粘包现象

TCP粘包问题解析与解决方案

udp协议是不存在粘包现象的,因为它文件的传输方式就是面向包的

tcp协议是有可能出现粘包现象的,它存在粘包的情况有两种:

a.连续发送小包,如果出现网络延迟现象的话,两次发送的消息会一次性被接收

b.如果一次性发送的 ,文件过大,一般来说文件一次性传输大小为1500左右,我们定义接收的大小为1024.文件过大的话就会进行切割

在缓冲区等待发送,有可能会和下一个文件一起被接收.

 

面对粘包有两种方式:

(1)问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端发一个确认消息给发送端,然后发送端再发送过来后面的真实内容,接收端再来一个死循环接收完所有数据。

server代码

import socket,subprocess
ip_port=('127.0.0.1',8080)
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

s.bind(ip_port)
s.listen(5)

while True:
    conn,addr=s.accept()
    print('客户端',addr)
    while True:
        msg=conn.recv(1024)
        if not msg:break
        res=subprocess.Popen(msg.decode('utf-8'),shell=True,\
                            stdin=subprocess.PIPE,\
                         stderr=subprocess.PIPE,\
                         stdout=subprocess.PIPE)
        err=res.stderr.read()
        if err:
            ret=err
        else:
            ret=res.stdout.read()
        data_length=len(ret)
        conn.send(str(data_length).encode('utf-8'))
        data=conn.recv(1024).decode('utf-8')
        if data == 'recv_ready':
            conn.sendall(ret)
    conn.close()

tcp_server.py
tcp_server.py

 cilent代码

import socket,time
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080))

while True:
    msg=input('>>: ').strip()
    if len(msg) == 0:continue
    if msg == 'quit':break

    s.send(msg.encode('utf-8'))
    length=int(s.recv(1024).decode('utf-8'))
    s.send('recv_ready'.encode('utf-8'))
    send_size=0
    recv_size=0
    data=b''
    while recv_size < length:
        data+=s.recv(1024)
        recv_size+=len(data)


    print(data.decode('utf-8'))
tcp_cilent.py

(2)解决方案(二):

通过struck模块将需要发送的内容的长度进行打包,打包成一个4字节长度的数据发送到对端,对端只要取出前4个字节,然后对这四个字节的数据进行解包,接收数据端就知道我们总数据长度是多少,然后同一文件就会进行一次封装.
server端
import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

phone.bind(('127.0.0.1',8080))
phone.listen(5)

while True:
    conn,addr=phone.accept()
    while True:
        cmd=conn.recv(1024)
        if not cmd:break
        print('cmd: %s' %cmd)

        res=subprocess.Popen(cmd.decode('utf-8'),
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE)
        err=res.stderr.read()
        print(err)
        if err:
            back_msg=err
        else:
            back_msg=res.stdout.read()

        headers={'data_size':len(back_msg)}
        head_json=json.dumps(headers)
        head_json_bytes=bytes(head_json,encoding='utf-8')

        conn.send(struct.pack('i',len(head_json_bytes))) #先发报头的长度
        conn.send(head_json_bytes) #再发报头
        conn.sendall(back_msg) #在发真实的内容

    conn.close()
ctp_server.py

cilent端口

import struct,json,socket

ip_port=('127.0.0.1',8080)
client=socket(AF_INET,SOCK_STREAM)
client.connect(ip_port)

while True:
    cmd=input('>>: ')
    if not cmd:continue
    client.send(bytes(cmd,encoding='utf-8'))

    head=client.recv(4)
    head_json_len=struct.unpack('i',head)[0]
    head_json=json.loads(client.recv(head_json_len).decode('utf-8'))
    data_len=head_json['data_size']

    recv_size=0
    recv_data=b''
    while recv_size < data_len:
        recv_data+=client.recv(1024)
        recv_size+=len(recv_data)

    #print(recv_data.decode('utf-8'))
    print(recv_data.decode('gbk')) #windows默认gbk编码
tcp_client.py

 

转载于:https://www.cnblogs.com/lowen107/p/10027806.html

<think> 嗯,用户问的是UDP传输是否存在粘包现象。这个问题在之前的引用里其实有明确答案,但需要仔细梳理一下。 用户可能是个网络编程的初学者,或者在实际开发中遇到了数据接收异常的情况。ta特意强调“是否存在”,说明可能遇到了类似粘包的现象但不确定原因。从ta引用的资料来看,已经做过一些调研,但被不同说法搞糊涂了——引用1和引用3说UDP有粘包,引用2和引用4又说没有。 先看技术本质:UDP每个数据报都是独立的传输单元,有明确边界。内核的recvfrom调用必然返回完整数据报,这是RFC768规定的。但用户引用的材料1提到“无意中基于一发一收的假设”,这其实是指应用层逻辑错误:比如发送方连续发两个包,接收方缓冲区可能同时收到,如果错误地一次读取过多数据就会混包。 用户可能混淆了网络层特性与应用层处理。需要强调:UDP本身不粘包,但应用层代码质量可能导致类似现象。比如接收方用200字节缓冲区收两个100字节的包,系统会返回200字节数据,如果开发者错误认为这200字节是单个包,就相当于人为制造了“粘包”。 另外注意到用户引用3提到“缓冲区带来粘包问题”,这其实是对TCP的误读移植到UDP上了。TCP的流式传输才需要应用层自己划分边界,UDP根本不需要。 最后决定用表格对比TCP和UDP的数据边界特性,这样最清晰。还要提醒用户:虽然UDP不粘包,但乱序和丢包问题比TCP严重,这也是为什么视频通话通常用UDP但需要应用层加序号。 </think>根据网络传输原理和引用资料,关于UDP是否存在粘包问题的解答如下: ### 核心结论 **UDP协议本身不存在粘包问题**。这是由UDP的协议特性决定的: 1. **面向数据报传输**(引用[4]) UDP每个数据包自带消息边界,接收方每次读取必然获得**完整的独立数据报**,不会出现多个数据包粘连的情况。 $$ \text{UDP数据报} = \text{包头(8字节)} + \text{应用层数据} $$ 2. **明确的长度标识**(引用[4]) UDP头部包含16位长度字段,明确标识整个数据包的长度,确保接收方能准确分割数据。 ### 与TCP的关键差异对比 | **特性** | **TCP** | **UDP** | |--------------------|------------------------------------|----------------------------------| | 传输模式 | 面向字节流(无消息边界) | 面向数据报(有消息边界) | | 粘包风险 | 存在(需应用层处理) | **不存在** | | 接收端处理 | 可能合并多个数据包 | 每次读取一个完整数据报 | | 头部长度标识 | 无 | 16位长度字段明确标识包长度 | ### 常见误解分析 1. **"UDP粘包"的实质**(引用[1][3]) 当观察到UDP数据粘连时,本质是**应用层逻辑缺陷**: - 发送方过小的数据包被网卡/NIC合并发送(与协议无关) - 接收方使用过大缓冲区一次性读取多个**独立数据报** ```c // 错误示例:用200字节缓冲区读取两个100字节的UDP包 char buf[200]; recvfrom(sockfd, buf, 200, 0, ...); // 实际收到两个独立数据报(共200字节) ``` 2. **解决方案**(引用[4]) - 发送端:控制单次发送的数据报不超过MTU(通常≤1472字节) - 接收端:按**最大预期包长**分配缓冲区并逐包处理 ```python # 正确做法:按包处理 while True: data, addr = sock.recvfrom(1472) # 每次读取一个完整UDP包 process_packet(data) # 独立处理每个数据报 ``` ### 典型场景验证 1. **连续发送小包** - 发送方:连续发送 `A`(50字节)、`B`(50字节) - 接收方:可能触发**两次接收**(收到`A`和`B`)或**一次接收100字节** - **关键**:100字节数据实为两个独立包,需按发送长度分割,**非协议层粘连** 2. **丢包与乱序** UDP虽不粘包,但可能发生: - 数据报丢失(需应用层重传) - 乱序到达(需序列号排序) ### 总结 UDP协议设计**从根本上避免了粘包**,其数据报边界特性确保每次接收均为完整独立的数据单元。观察到的"粘连"本质是应用层未正确处理数据报边界所致[^1][^4]。对于需严格消息边界的场景(如DNS查询、实时音视频),UDP仍是更简洁高效的选择。 --- **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值