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],addr[1]))
    while True:
        try:
            cmd=conn.recv(1024)
            if not cmd:break
            #执行命令
            obj=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            conn.send(stdout+stderr)
        except Exception:
            break
    conn.close()
phone.close()

客户端:

import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect((‘127.0.0.1‘,8090))
while True:
    cmd=input(‘>>>‘).strip()
    if not cmd:continue
    phone.send(cmd.encode(‘utf-8‘))
    res=phone.recv(1024)
    print(res.decode(‘gbk‘))
phone.close()

注意注意注意:

res=subprocess.Popen(cmd.decode(‘utf-8‘),
shell=True,
stderr=subprocess.PIPE,
stdout=subprocess.PIPE)

的结果的编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码,且只能从管道里读一次结果。

2、粘包现象

  只有TCP有粘包现象,TCP协议是面向流的协议,这也是容易出现粘包问题的原因。例如基于tcp的套接字客户端往服务端上传文件,发送时文件内容是按照一段一段的字节流发送的,在接收方看了,根本不知道该文件的字节流从何处开始,在何处结束。所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

  此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

两种情况下会发生粘包。

  发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)

  接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)

解决办法1:

服务端:

import socket
import subprocess
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind((‘127.0.0.1‘,8090))
phone.listen(5)
print(‘starting...‘)
while True:
    conn,addr=phone.accept()
    print(‘IP:%s,PORT:%s‘%(addr[0],addr[1]))
    while True:
        try:
            cmd=conn.recv(1024)
            #执行命令cmd
            obj=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            #发送真实数据的描述信息:长度
            header=struct.pack(‘i‘,len(stdout)+len(stderr))  #i模式为针对整数型数据,下输出的结果为4
            conn.send(header)
           # 发送真实数据
            conn.send(stdout)
            conn.send(stderr)
        except Exception:
            break
    conn.close()
phone.close()

客户端:

import socket
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect((‘127.0.0.1‘,8090))
while True:
    cmd=input(‘>>>‘).strip()
    phone.send(cmd.encode(‘utf-8‘))
    header=phone.recv(4) #指定接收4个字节
    total_size=struct.unpack(‘i‘,header)[0]#对接收的4个字节数据进行解包得到待接收数据大小的元组:(数据大小,)
    #循环接收数据
    total_data=b‘‘
    recv_size=0
    while recv_size<total_size:
        recv_data=phone.recv(1024)
        total_data+=recv_data
        recv_size+=len(recv_data)
    print(total_data.decode(‘gbk‘))
phone.close()

  由于模块struct的pack方法中使用的i类型存在整数无限大时会出现报错的弊端,故提出如下解决方案:

解决办法2:

服务端:

import socket
import subprocess
import struct
import json

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.bind((‘127.0.0.1‘,8090))
phone.listen(5)
print(‘starting...‘)
while True:
    conn,addr=phone.accept()
    print(‘IP:%s,PORT:%s‘%(addr[0],addr[1]))
    while True:
        try:
            cmd=conn.recv(1024)
            if not cmd:break
            obj=subprocess.Popen(cmd.decode(‘utf-8‘),shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            #制作报头
            header_dic={‘filename‘: ‘a.txt‘,
                          ‘total_size‘: len(stdout)+len(stderr),
                          ‘md5‘: ‘asdfa123xvc123‘}
            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 Exception:
            break
    conn.close()
phone.close()

客户端:

import socket
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect((‘127.0.0.1‘,8090))
while True:
    cmd=input(‘>>>‘).strip()
    if not cmd:continue
    phone.send(cmd.encode(‘utf-8‘))
    # 接收报头内容长度
    obj=phone.recv(4)
    header_size=struct.unpack(‘i‘,obj)[0]
    #接收报头字典
    header_bytes=phone.recv(header_size)
    header_json=header_bytes.decode(‘utf-8‘)
    header_dic=json.loads(header_json)
    total_size=header_dic[‘total_size‘]

    total_data=b‘‘
    recv_size=0
    while recv_size<total_size:
        recv_data=phone.recv(1024)
        total_data+=recv_data
        recv_size+=len(recv_data)
    print(total_data.decode(‘gbk‘))
phone.close()
时间: 2024-10-30 03:22:24

python学习_day30_基于tcp协议的粘包现象的相关文章

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

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

python学习_day31_基于udp协议编程

一.作业解析 基于tcp文件流协议执行客户端的下载命令. 服务端: from socket import * import json import struct import os import hashlib def get(filename,conn): header_dic={ 'filename':os.path.basename(filename), #获得去除路径的纯文件名 'data_size':os.path.getsize(filename) #获得文件的字节大小 } head

tcp协议下粘包问题的产生及解决方案

1.粘包产生原因: (1)TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段.若连续几次需要send的数据都很少,通常TCP会根据优化算法(Nagle)把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据: (2)接收方不知道消息之间的界限,不知道一次性提取多少字节的数据:接收时有字节的限制,如果超过这个限制没有接收完的会留在 操作系统缓存,下次再执行命令获取结果时,优先收取上次命令结果残留的信息: 注:UDP是无连接的,面向消息的,提供高效率服务.不会使用

tcp协议产生-粘包问题的解决方案

客户端 1 客户端 2 3 from socket import * 4 import json,struct 5 6 7 client=socket(AF_INET,SOCK_STREAM) 8 client.connect(('127.0.0.1',8080)) 9 10 11 while True: 12 cmd=input('>>>').strip() 13 if len(cmd)==0:continue 14 client.send(cmd.encode('utf8')) 15

Python网络编程02/基于TCP协议的socket简单的通信

目录 Python网络编程02/基于TCP协议的socket简单的通信 1.昨日内容回顾 2.socket 2.1 socket套接字 2.2 基于TCP协议的socket简单通信 Python网络编程02/基于TCP协议的socket简单的通信 1.昨日内容回顾 1.单播:单独联系某一个人 2.广播:给所有人发送消息(群发) 3.比特流:bit就是0101跟水流一样的源源不断的发送01010101 4.以太网协议:将数据进行分组:一组称之为一帧,数据报 head|data head:18字节:

python中基于tcp协议的通信(数据传输)

tcp协议:流式协议(以数据流的形式通信传输).安全协议(收发信息都需收到确认信息才能完成收发,是一种双向通道的通信) tcp协议在OSI七层协议中属于传输层,它上承用户层的数据收发,下启网络层.数据链路层.物理层.可以说很多安全数据的传输通信都是基于tcp协议进行的. 为了让tcp通信更加方便需要引入一个socket模块(将网络层.数据链路层.物理层封装的模块),我们只要调用模块中的相关接口就能实现传输层下面的繁琐操作. 简单的tcp协议通信模板:(需要一个服务端和一个客户端) 服务端: fr

Learning-Python【28】:基于TCP协议通信的套接字

什么是 Socket Socket 是应用层与 TCP/IP 协议通信的中间软件抽象层,它是一组接口.在设计模式中,Socket 其实就是一个门面模式,它把复杂的 TCP/IP 协议族隐藏在 Socket 接口后面,对用户来说,一组简单的接口就是全部,让 Socket 去组织数据,以符合指定的协议. 所以,我们无需深入理解 TCP/UDP 协议,socket 已经为我们封装好了,我们只需要遵循 socket 的规定去编程,写出的程序自然就是遵循 TCP/UDP 标准的. 套接字的分类: 基于文件

用c++开发基于tcp协议的文件上传功能

用c++开发基于tcp协议的文件上传功能 2005我正在一家游戏公司做程序员,当时一直在看<Windows网络编程> 这本书,把里面提到的每种IO模型都试了一次,强烈推荐学习网络编程的同学阅读,比 APUE 讲的更深入 这是某个银行广告项目(p2p传输视频)的一部分 IO模型采用的阻塞模式,文件一打开就直接上传 用vc 2003编译,生成win32 dll 麻雀虽小五脏俱全,CSimpleSocket,CReadStream dll 输出一虚类 extern "C" __d

Windows下基于TCP协议的大文件传输(流形式)

简单实现TCP下的大文件高效传输 在TCP下进行大文件传输,不像小文件那样直接打包个BUFFER发送出去,因为文件比较大可能是1G,2G或更大,第一效率问题,第二TCP粘包问题.针对服务端的设计来说就更需要严紧些.下面介绍简单地实现大文件在TCP的传输应用. 粘包出现原因:在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程) 1 发送端需要等缓冲区满才发送出去,造成粘包 2 接收方不及时接收缓冲区的包,造成多个包接收 解决办法: 为了避免粘包现象,可采取以下几种措