粘包现象(存在于tcp中)

多个包 多个命令的结果 粘到一起了 因为recv(1024)1024限制了导致的结果

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

参考:http://www.cnblogs.com/linhaifeng/articles/6129246.html

粘包底层原理分析:
1、运行一个软件和硬盘、内存、cpu这三个硬件有关

2、启动程序:硬盘程序加载到内存启动一个软件就占一个内存空间
  操作系统本身有一个内存空间
  操作系统所占得到内存空间和软件的内存空间彼此互相隔离

注:只有TCP有粘包现象,UDP永远不会粘包。



3、send、recv的底层原理

应用程序通过send将自己数据发给操作系统,操作系统调用相应硬件中的数据及程序,通过网卡传送给操作系统(recv对应的操作系统),操作系统通过相应程序将数据传送给secv
  1、send发到数据到服务端os的内存 # 慢
  2、os的内存 copy 给程序 # 快
站在应用程序角度上:
  send: 1.数据发给本地的os # 耗时短一些
  recv:1.通知os收数据 2.os内存数据copy到应用程序内存中 # 耗时长一些

4、send recv 总结:
a、不管是recv还是send都不是直接接收对方的数据,而是操作自己的操作系统内存--->不是一个send对应一个recv # 一发可以对应多收 一收对应多发
b、recv:
  wait data 耗时非常长
  copy data
     send:
  copy data
c、数据量比较小 时间间隔比较短就合并成一个包,再发
使用了优化方法(Nagle算法)

5.recv 有关部门建议的不要超过 8192,再大反而会出现影响收发速度和不稳定的情况


解决粘包现象

用struct模块

import struct

# res = struct.pack("i", 1230)  # 第一个参数代表格式i(int)
# print(res, type(res), len(res))
#
# obj = struct.unpack("i", res)
# print(obj[0])

# clienr.recv(4)

res = struct.pack("l", 1200000000)  # 第一个参数代表格式l(long)
print(res, type(res), len(res))

解决粘包解释加报头


解决粘包(简单版)

服务端

import socket
import subprocess
import struct

# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # (如果机器中存在,重新用端口)应对端口占用报错情况
# 2、绑定手机卡
phone.bind(("127.0.0.1", 9909))   # 127.0.0.1本地地址,端口范围0-65535:其中0-1024给操作系统使用

# 3、开机
phone.listen(5)   # 5代表最大挂起连接数

# 4、等电话连接
print("starting...")
while True:  # 循环链接
    conn, client = phone.accept()  # conn套接字对象

# 5、收、发消息
    while True:    # 通讯循环
        try:
            # a、接收命令  (命令:执行系统命令)
            cmd = conn.recv(8096)  # 收1024个字节,接受数据的最大数。单位是bytes
           # if not data: break  # 仅适用于Linux操作系统(客户端断开),win 用try...except
            # b、执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            stdout = obj.stdout.read()
            stderr = obj.stderr.read()

            # c、把命令的结果返回给客户端
            # 第一步:制作固定长度的报头(import struct)
            total_size = len(stdout) + len(stderr)
            header = struct.pack("i", total_size)

            # 第二步:把报头(固定长度)发送给客户端
            conn.send(header)

            # 第二步:再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionRefusedError:
            break

# 6、挂电话
    conn.close()

# 7、关机
phone.close()

客户端

import socket
import struct

# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2、打电话
phone.connect(("127.0.0.1", 9909))  # phone相当于服务端的conn

# 3、发、收消息
while True:
    # a、发命令
    cmd = input(">> ").strip()
    if not cmd:
        continue
    phone.send(cmd.encode("utf-8"))

    # b、拿命令结果并打印
    # 第一步:先收报头
    header = phone.recv(4)

    # 第二步:从报头中解析出对真实数据的描述信息(数据的长度)
    total_size = struct.unpack("i", header)[0]
    # 第三步:接受真实的数据
    recv_size = 0
    recv_data = b""
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)
    print(recv_data.decode("gbk"))   # 系统发回的结果

# 4、关闭
phone.close()

  


解决粘包(终极版)

服务端

import socket
import subprocess
import struct
import json

# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)  # (如果机器中存在,重新用端口)应对端口占用报错情况
# 2、绑定手机卡
phone.bind(("127.0.0.1", 9909))   # 127.0.0.1本地地址,端口范围0-65535:其中0-1024给操作系统使用

# 3、开机
phone.listen(5)   # 5代表最大挂起连接数

# 4、等电话连接
print("starting...")
while True:  # 循环链接
    conn, client = phone.accept()  # conn套接字对象

# 5、收、发消息
    while True:    # 通讯循环
        try:
            # a、接收命令  (命令:执行系统命令)
            cmd = conn.recv(8096)  # 收1024个字节,接受数据的最大数。单位是bytes
           # if not data: break  # 仅适用于Linux操作系统(客户端断开),win 用try...except
            # b、执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode("utf-8"), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
            stdout = obj.stdout.read()
            stderr = obj.stderr.read()

            # c、把命令的结果返回给客户端
            # 第一步:制作固定长度的报头(import struct)
            header_dic = {
                "filename": "a.text",
                "md5": "xxdxxx",
                "total_size": len(stdout) + len(stderr)
            }
            header_json = json.dumps(header_dic)

            header_bytes = header_json.encode("utf-8")

            # 第二步:先发送报头的长度
            conn.send(struct.pack("i", len(header_bytes)))

            # 第三步:再发报头
            conn.send(header_bytes)

            # 第四步:再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionRefusedError:
            break

# 6、挂电话
    conn.close()

# 7、关机
phone.close()

客户端

import socket
import struct
import json

# 1、买手机
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 2、打电话
phone.connect(("127.0.0.1", 9909))  # phone相当于服务端的conn

# 3、发、收消息
while True:
    # a、发命令
    cmd = input(">> ").strip()
    if not cmd:
        continue
    phone.send(cmd.encode("utf-8"))

    # b、拿命令结果并打印
    # 第一步:先收报头的长度
    header = phone.recv(4)
    header_size = struct.unpack("i", header)[0]

    #第二步:再接收报头信息
    header_bytes = phone.recv(header_size)

    # 第三步:从报头中解析出对真实数据的描述信息
    header_json = header_bytes.decode("utf-8")
    header_dic = json.loads(header_json)
    print(header_dic)
    total_size = header_dic["total_size"]

    # 第四步:接受真实的数据
    recv_size = 0
    recv_data = b""
    while recv_size < total_size:
        res = phone.recv(1024)
        recv_data += res
        recv_size += len(res)
    print(recv_data.decode("gbk"))   # 系统发回的结果

# 4、关闭
phone.close()

  

原文地址:https://www.cnblogs.com/fantsaymwq/p/10051260.html

时间: 2024-07-29 14:08:48

粘包现象(存在于tcp中)的相关文章

什么是粘包现象

简单远程执行命令程序开发(30分钟) 是时候用户socket干点正事呀,我们来写一个远程执行命令的程序,写一个socket client端在windows端发送指令,一个socket server在Linux端执行命令并返回结果给客户端 执行命令的话,肯定是用我们学过的subprocess模块啦,但注意注意注意: res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdout=subproce

5 粘包现象与解决方案

简单远程执行命令程序开发 是时候用户socket干点正事呀,我们来写一个远程执行命令的程序,写一个socket client端在windows端发送指令,一个socket server在Linux端执行命令并返回结果给客户端 执行命令的话,肯定是用我们学过的subprocess模块啦,但注意注意注意: res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdout=subprocess.PIP

python学习_day30_基于tcp协议的粘包现象

1.基于远程执行命令的程序 需用到subprocess模块 服务端: #1.执行客户端发送的指令 import socket import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.bind(('127.0.0.1',8090)) phone.listen(5) while True: conn,addr=phone.accept() print('IP:%s PORT:%s' %(addr[0

基于tcp协议下粘包现象和解决方案

一.缓冲区 每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区.write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器.一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情.TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,这取决

什么是粘包? socket 中造成粘包的原因是什么? 哪些情况会发生粘包现象?

只有TCP有粘包现象,UDP永远不会粘包! 粘包:在接收数据时,一次性多接收了其它请求发送来的数据(即多包接收).如,对方第一次发送hello,第二次发送world, 在接收时,应该收两次,一次是hello,一次是world,但事实上是一次收到helloworld,一次收到空,这种现象叫粘包. 原因 粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的. 什么情况会发生: 1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据很小,会合到一起,

python3 tcp的粘包现象和解决办法

服务器端 import socket sk = socket.socket() sk.bind(("127.0.0.1", 6666)) sk.listen() conn, address = sk.accept() def my_send(msg): bs = msg.encode("utf-8") len_str = format(len(bs), "04d") # 定长4位 conn.send(len_str.encode("ut

网络编程-之------粘包现象

一.什么是粘包 须知:只有TCP有粘包现象,UDP永远不会粘包 粘包不一定会发生 如果发生了:1.可能是在客户端已经粘了 2.客户端没有粘,可能是在服务端粘了 首先需要掌握一个socket收发消息的原理 应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因.(因为TCP是流式协议,不知道啥时候开始,啥时候结束).而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提

粘包现象

一.基于udp的套接字 udp是无链接的,先启动哪一端都不会报错 udp服务端: ss = socket() #创建一个服务器的套接字 ss.bind() #绑定服务器套接字 while True : #服务器无限循环 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送) ss.close() # 关闭服务器套接字 udp客户端: cs = socket() # 创建客户套接字 while True : # 通讯循环 cs.sendto()/cs.recvfrom

Python--网络编程-----粘包现象

一.为了防止出现端口占用的情况, linux中可以使用pkill -9 python windows系统中使用taskkill python 二.什么是粘包现象 1.多个包(也就是多个命令的执行结果)粘在一起的现象,叫做粘包现象 2.代码示例如下: 服务端代码: 1 import socket 2 import subprocess 3 4 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 5 # phone.setsockopt