TCp协议粘包解决

半链接数:

三次握手没有完成,称为半链接

原因:1.客户端恶意没有接收服务器返回的消息,没有返回第三次握手消息。这样如果大量的客户端这么做就是洪水攻击。2.服务器没空及时处理客户端的请求

socket中listen(半链接最大数量)

粘包问题:

TCP流式协议, 数据之间没有分界, 就像水 一杯水和一杯牛奶倒在一起了!

UDP 用户数据报协议

粘包 仅发生在TCP协议中

  1. 发送端 发送的数据量小 并且间隔短 会粘

  2. 接收端 一次性读取了两次数据的内容 会粘

  3. 接收端 没有接收完整 剩余的内容 和下次发送的粘在一起

无论是那种情况,其根本原因在于 接收端不知道数据到底有多少

解决方案就是 提前告知接收方 数据的长度

解决方案:

先发长度给对方,再发真实数据

发送端

1.使用struct 将真实数据的长度转为固定的字节数据

2.发送长度数据

3.发送真实数据

接收端

1.先收长度数据 字节数固定

2.再收真实数据 真实可能很长 需要循环接收

发送端和接收端必须都处理粘包 才算真正的解决了

 

#    ==================================================客户端
import socket
from 二_CMD程序 import smallTool
import struct

client = socket.socket()
try:
    client.connect(("127.0.0.1",1688))
    print("链接成功!")
    while True:
        msg = input("请输入要执行指令:").strip()
        if msg == "q": break
        if not msg: continue
        # 发送指令
        # 先发长度
        len_bytes = struct.pack("q",len(msg.encode("utf-8")))
        client.send(len_bytes)
        # 在发指令
        client.send(msg.encode("utf-8"))

        data = smallTool.recv_data(client)
        print(data.decode("GBK"))

    client.close()
except ConnectionRefusedError as e:
    print("链接服务器失败了!",e)
except ConnectionResetError as e:
    print("服务器挂了!", e)
    client.close()

import socket
import subprocess
import struct
from 二_CMD程序 import  smallTool

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",1688))
server.listen()
# back

while True:
    # socket,addr一个元组 客户端的ip和port
    client,addr = server.accept()
    print("客户端链接成功!")
    # 循环收发数据
    while True:
        try:
            cmd = smallTool.recv_data(client)
            if not cmd:
                break
            print(cmd)

            p = subprocess.Popen(cmd.decode("utf-8"),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            # 不要先读err错误信息  它会卡主  原因不详  linux不会有问题  tasklist  netstat - ano啥的
            data = p.stdout.read()
            err_data = p.stderr.read()

            len_size = len(data) + len(err_data)
            print("服务器返回了: %s " %  len_size)

            len_bytes = struct.pack("q",len_size)

            # 在发送真实数据前先发送 长度
            client.send(len_bytes)

            # 返回的结果刚好就是二进制
            # 发送真实数据
            client.send(data + err_data)


        except ConnectionResetError as e:
            print("客户端了挂了!",e)
            break
    client.close()

#server.close()

自定义报头:

当需要在传输数据时 传呼一些额外参数时就需要自定义报头

报头本质是一个json 数据

具体过程如下:

发送端

1 发送报头长度

2 发送报头数据 其中包含了文件长度 和其他任意的额外信息

3 发送文件内容

 

接收端

1.接收报头长度

2.接收报头信息

3.接收文件内容

import socket
import os
import struct
import json
"""
客户端接链成功我就给你发个文件过去  
固定的文件下载

"""

server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",1688))
server.listen()
# back

while True:
    # socket,addr一个元组 客户端的ip和port
    client,addr = server.accept()
    print("客户端链接成功!")
    f = None
    try:

        path = r"F:\2.半链接数.mp4"
        file_size = os.path.getsize(path)


        # 我想把文件名发过去
        file_info = {"file_name":"半链接数.mp4","file_size":file_size,"md5":"xxxxxxxxx"}

        json_str = json.dumps(file_info).encode("utf-8")

        # 发送报头长度
        client.send(struct.pack("q",len(json_str)))

        # 发报头
        client.send(json_str)

        # 发文件了
        # 发送文件数据
        f = open(path,"rb")
        # 循环发送文件内容   每次发2048
        while True:
            temp = f.read(2048)
            if not temp:
                break
            client.send(temp)
        print("文件发送完毕!")

    except Exception as e:
        print("出问题了",e)
    finally:
        if f:f.close()
    client.close()

    # 无论是否抛出异常 文件都要关闭

#server.close()

# 用户可以指定要下载什么文件 FTP
"""
客户端输入指令
服务器接收指令并执行  最后返回执行结果
"""

import socket
import struct
import json

client = socket.socket()
try:
    client.connect(("127.0.0.1",1688))
    print("链接成功!")

    # 1.先收报头长度
    head_size = struct.unpack("q",client.recv(8))[0]

    # 2.收报头数据
    head_str = client.recv(head_size).decode("utf-8")
    file_info = json.loads(head_str)
    print("报头数据:",file_info)
    file_size = file_info.get("file_size")
    file_name = file_info.get("file_name")


    # 3.再收文件内容
    # 已接收大小
    recv_size = 0
    buffer_size = 2048
    f = open(file_name,"wb")
    while True:
        if file_size - recv_size >= buffer_size:
            temp = client.recv(buffer_size)

        else:
            temp = client.recv(file_size - recv_size)
        f.write(temp)
        recv_size += len(temp)
        print("已下载:%s%%" % (recv_size / file_size * 100))
        if recv_size == file_size:
            break
    f.close()
except ConnectionRefusedError as e:
    print("链接服务器失败了!",e)

 

posted @ 2019-05-30 14:59  帅气逼人23  阅读(161)  评论(0编辑  收藏  举报