20 Apr 18 粘包问题及解决方法

20 Apr 18
一、上节课复习
1、 TCP(建立的是一个双向连接)三次握手建连接,四次挥手断连接
三次握手:
   c----syn=1 seq=x--->s
   s----ack=1+x syn=1 seq=y--->c
   c----ack=1+y------->s
   四次挥手:
   s------fin=1---------->c
   c------>ack=1--------->s
   c------>fin=1--------->s
   s------>ack=1--------->c
   
127.0.0.1: 本地回环地址
无论收发(send,receive)都是和自己的操作系统直接交互,由操作系统接着调硬件将信息传到对方的操作系统。
也是基于以上原因,收发不是一一对应的。
 
2、socket
   socket是位于应用层与传输层之间的一个抽象层,专门把传输层以下的协议封装成接口提供给应用层使用。应用只需要调用socket的接口或者说按照socket的标准编写程序,写出的程序自然是遵循tcp/ip协议。
 
二、粘包问题
TCP又称流式协议,有两个特性:
a、 像流水一样源源不断
b、 优化传输机制,用NAGLE算法,将时间间隔短的数据量少的数据攒成一波
以上两个特性导致了粘包问题
 
解决粘包问题的思路是知道发多少,然后收多少
注:如果用time.sleep解决,把这一端解决了,有可能在另一端发生粘包
 
三、解决粘包问题(基本板)
a、 struct模块
1)把整型数字转成bytes类型
2)转成的bytes是固定长度的
 
import struct
res=struct.pack(‘i‘,20332)   # ‘i’ --integer
print(res,len(res)) 
res2=struct.unpack(‘i‘,res)
print(res2[0])
 
b、 服务端
from socket import *
import subprocess
import struct
 
server=socket(AF_INET,SOCK_STREAM)
server.bind((‘127.0.0.1‘,8080))
server.listen(5)
 
while True:
    conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
    print(client_addr)
    while True:
        try:
            cmd=conn.recv(1024)
            obj=subprocess.Popen(cmd.decode(‘utf-8‘),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
 
            # 1、制作固定长度的报头
            total_size=len(stdout) + len(stderr)
            header=struct.pack(‘i‘,total_size)
 
            # 2、发送报头
            conn.send(header)
 
            #3、发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
 
    conn.close()
server.close()
 
c、 客服端
from socket import *
import struct
 
client=socket(AF_INET,SOCK_STREAM)
client.connect((‘127.0.0.1‘,8080))
# print(client)
 
while True:
    cmd=input(‘>>>: ‘).strip()
    if not cmd:continue
    client.send(cmd.encode(‘utf-8‘))
    #1、先收固定长度的报头
    header=client.recv(4)
    #2、解析报头
    total_size=struct.unpack(‘i‘,header)[0]
    print(total_size) #1025
    #3、根据报头内的信息,收取真实的数据
 
    recv_size=0
    res=b‘‘
    while recv_size < total_size:
        recv_data=client.recv(1024)
        res+=recv_data
        recv_size+=len(recv_data)
 
    print(res.decode(‘gbk‘))
client.close()
 
四、解决粘包问题(终极版)
a、 struct模块
import struct
import json
 
header_dic = {
    ‘total_size‘: 3122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222121,
    ‘md5‘: ‘123svsaef123sdfasdf‘,
    ‘filename‘: ‘a.txt‘
}
 
header_json=json.dumps(header_dic)
# print(header_json,type(header_json))
header_bytes=header_json.encode(‘utf-8‘)
header_size=len(header_bytes)
 
print(header_size)
obj=struct.pack(‘i‘,header_size)
print(obj,len(obj))
 
b、 服务端
from socket import *
import subprocess
import struct
import json
 
server=socket(AF_INET,SOCK_STREAM)
server.bind((‘127.0.0.1‘,8080))
server.listen(5)
 
while True:
    conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
    print(client_addr)
    while True:
        try:
            cmd=conn.recv(1024)
            obj=subprocess.Popen(cmd.decode(‘utf-8‘),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
 
            # 1、制作报头
            header_dic={
                ‘total_size‘:len(stdout) + len(stderr),
                ‘md5‘:‘123svsaef123sdfasdf‘,
                ‘filename‘:‘a.txt‘
            }
            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode(‘utf-8‘)
 
            # 2、先发送报头的长度
            header_size=len(header_bytes)
            conn.send(struct.pack(‘i‘,header_size))
 
            # 3、发送报头
            conn.send(header_bytes)
 
            # 4、发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
 
    conn.close()
server.close()
 
c、 客户端
from socket import *
import struct
import json
 
client=socket(AF_INET,SOCK_STREAM)
client.connect((‘127.0.0.1‘,8080))
# print(client)
 
while True:
    cmd=input(‘>>>: ‘).strip()
    if not cmd:continue
    client.send(cmd.encode(‘utf-8‘))
    #1、先收报头的长度
    header_size=struct.unpack(‘i‘,client.recv(4))[0]
 
    #2、接收报头
    header_bytes=client.recv(header_size)
 
    #3、解析报头
    header_json=header_bytes.decode(‘utf-8‘)
    header_dic=json.loads(header_json)
    print(header_dic)
 
    total_size=header_dic[ ‘total_size‘]
    # print(total_size) #1025
    #4、根据报头内的信息,收取真实的数据
 
    recv_size=0
    res=b‘‘
    while recv_size < total_size:
        recv_data=client.recv(1024)
        res+=recv_data
        recv_size+=len(recv_data)
 
    print(res.decode(‘gbk‘))
client.close()
 
五、远程执行命令的程序
a、 客户端
from socket import *
 
client=socket(AF_INET,SOCK_STREAM)
client.connect((‘127.0.0.1‘,8080))
# print(client)
 
while True:
    cmd=input(‘>>>: ‘).strip()
    if not cmd:continue
    client.send(cmd.encode(‘utf-8‘))
    # print(‘has send‘)
    res=client.recv(14744)
    # print(‘has recv‘)
    print(len(res))
    print(res.decode(‘gbk‘))
 
client.close()
 
b、 服务端
from socket import *
import subprocess
 
server=socket(AF_INET,SOCK_STREAM)
server.bind((‘127.0.0.1‘,8080))
server.listen(5)
 
while True:
    conn,client_addr=server.accept() #(连接对象,客户端的ip和端口)
    print(client_addr)
    while True:
        try:
            cmd=conn.recv(1024)
            obj=subprocess.Popen(cmd.decode(‘utf-8‘),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE
                                 )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
 
            # 先发送数据的长度
            total_size=len(stdout) + len(stderr)
            conn.send(total_size)
 
            # 发送真实的数据
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break
 
    conn.close()
server.close()
#交互式命令或是cd。。一般要在一台机器上,不推荐远程调控。如若强行要求,不要直接执行,而是要模拟执行,并将模拟结果发回。

原文地址:https://www.cnblogs.com/zhangyaqian/p/py20180420.html

时间: 2024-10-31 15:24:52

20 Apr 18 粘包问题及解决方法的相关文章

网络编程 TCP协议:三次握手,四次回收,反馈机制 socket套接字通信 粘包问题与解决方法

TCP协议:三次握手,四次挥手 TCP协议建立双向通道. 三次握手, 建连接: 1:客户端向服务端发送建立连接的请求 2:服务端返回收到请求的信息给客户端,并且发送往客户端建立连接的请求 3:客户端接收到服务端发来的请求,返回接成功给服务端,完成双向连接 第一客戶向服务端发送请求,请求建立连接 服务端同客户端的请求,并同时向客户端发送建立 连接的请求,最后客户端同意后建立 双向连接. C ----> S C <---- S - 反馈机制: 客户端往服务端发送请求,服务端必须返回响应, 告诉客户

Socket编程实践(5) --TCP粘包问题与解决

TCP粘包问题 因为TCP协议是基于字节流且无边界的传输协议, 因此非常有可能产生粘包问题, 问题描写叙述例如以下 watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvempmMjgwNDQxNTg5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" /> 对于Host A 发送的M1与M2两个各10K的数据块, Host B 接收数据的方式不确定, 有以下

Socket传输简单的信息以及粘包问题的解决

一.简单的socket程序--传输简短文字: # -*- coding: utf-8 -*- # -*- Author: WangHW -*- import socket whw_client = socket.socket(socket.AF_INET,socket.SOCK_STREAM) whw_client.connect(('127.0.0.1',9001)) while 1: cmd = input('>>>:').strip() if not cmd: continue w

python--subprocess,粘包现象与解决办法,缓冲区

一. subprocess 的简单用法 import subprocess sub_obj = subprocess.Popen( 'dir', #系统指令 shell=True, #固定方法 stdout=subprocess.PIPE, #标准输出 PIPE 管道,保存着指令的执行结果 stderr=subprocess.PIPE #标准错误输出 ) # dir 当前操作系统(Windows)的命令,会执行stdout print('正确输出',sub_obj.stdout.read().d

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

Socket编程实践(6) --TCP粘包原因与解决

流协议与粘包 粘包的表现 Host A 发送数据给 Host B; 而Host B 接收数据的方式不确定 粘包产生的原因 说明 TCP 字节流,无边界 对等方,一次读操作,不能保证完全把消息读完 UDP 数据报,有边界 对方接受数据包的个数是不确定的 产生粘包问题的原因分析 1.SQ_SNDBUF 套接字本身有缓冲区 (发送缓冲区.接受缓冲区) 2.tcp传送的端 mss大小限制 3.链路层也有MTU大小限制,如果数据包大于>MTU要在IP层进行分片,导致消息分割. 4.tcp的流量控制和拥塞控

20 网络编程 粘包现象与解决方案

1.https://www.cnblogs.com/guobaoyuan/p/6809447.html 发送数据包前 对包的长度进行计算 1. 比较low的方法是 len( package) 然后直接发送给接收端.这样会出现一个问题,就是接收端不知道你的这个 len(package)是几个字节,就也有可能会出现粘包问题. 2. 利用struct对包的长度打包为固定4个字节或8个字节. 3. struct.pack format参数为"i" 时只能打包长度为10的数字,那么还可以先将 长

记一次Maven发布Jar包中文乱码解决方法

Maven deploy 乱码 今天使用Maven发布Jar包时,发布功能都是正常的也成功上传到了仓库,就是项目跑越来后出中文中现了乱码: { "code": "SUCCESS", "success": true, "message": "鎿嶄綔鎴愬姛", "data": [ { "key": "app_force_login", "va

Eclipse 无法查看jar包文件源代码解决方法

1.打开第三方依赖包,源文件的快捷键:ctrl + mouseClick 2.由于我们下载的第三方jar 包,如Spring等相关的依赖包时,并没有附加下载相应的源文件,所以经常出现如图的这种问题. 解决方法如下: 1.jar包下都为编译生成的 .class 文件,所以安装相应的反编译插件,反编译为 .java 文件后,即可查看源码. 2.先下载jadClipse的jar包(反编译插件) 链接:sourceforge.net/projects/jadclipse/ 3.然后,将net.sf.ja