【问题标题】:tcp lost packet python [duplicate]tcp丢失数据包python [重复]
【发布时间】:2020-06-10 13:17:00
【问题描述】:

我正在使用 tcp 套接字在服务器和客户端之间转发数据(作为具有请求类型和数据的类)。当我从客户端发送 6 个订单对象时,服务器只得到 5 个订单对象,1 个丢失,但 tcp 是可靠的协议......(有时服务器得到 6 个但通常得到 5 个)我的问题是为什么一个对象丢失了通过tcp转发?

这是客户端代码

PORT = 3000
SIZE = 4096
HOST = '127.0.0.1'
soc = None
ing_map = None


def connect_to_client():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    global soc
    soc = s


def send_object(data):
    if soc is None:
        raise NotImplementedError  # There is no connection
    else:
        # make data as bytes
        msg = pickle.dumps(data)
        msg = bytes(f"{len(msg):<{HEADERSIZE}}", 'utf-8') + msg
        print(sys.getsizeof(msg))
        soc.send(msg)


def get_object(req):
    if soc is None:
        raise NotImplementedError  # There is no connection
    else:
        # unpickle the data
        send_object(req)
        if soc is None:
            raise NotImplementedError  # There is no connection
        else:
            # unpickle the data
            data = b''
            while True:
                part = soc.recv(SIZE)
                data += part
                if len(part) < SIZE:
                    break
            full_msg = data
            try:
                data = pickle.loads(full_msg[HEADERSIZE:])
            except EOFError:
                data = None
            return data


def send_order(order):
    if order is not None:
        send_object(Soc_request(Request.P_ORDER,order))


def main():

    global ing_map
    connect_to_client()
    ing_map = get_object(Soc_request(Request.G_ING_MAP, None))

    #send order
    burger = Burger(ing_map)
    salad = Salad(ing_map)
    burger.add_ingredient('ham')
    o1 = Order(Priority.NORMAL)
    o2 = Order(Priority.NORMAL)
    o3 = Order(Priority.VIP)
    o4 = Order(Priority.VIP)
    o5 = Order(Priority.PLUS)
    o6 = Order(Priority.PLUS)
    o1.meals_lst.append(burger)
    o1.meals_lst.append(burger)
    o1.meals_lst.append(burger)
    o3.meals_lst.append(burger)
    send_order(o1)
    send_order(o2)
    send_order(o3)
    send_order(o4)
    send_order(o5)
    send_order(o6)

    soc.close()

这是服务器代码

HOST = '127.0.0.1'
PORT = 3000
SIZE = 1000
HEADERSIZE = 10
WORKERS = 2

conn = None
addr = None
soc = None
order_m = None


def create_connection():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.bind((HOST, PORT))
    s.listen()
    global conn, addr
    global soc
    soc = s
    conn, addr = s.accept()


def send_object(data):
    if soc is None:
        raise NotImplementedError  # There is no connection
    else:
# make data as bytes
        global conn
        msg = pickle.dumps(data)
        msg = bytes(f"{len(msg):<{HEADERSIZE}}", 'utf-8') + msg
        conn.send(msg)


def get_object():
    global conn
    if conn is None:
        raise NotImplementedError  # There is no connection
    else:
        # unpickle the data
        data = b''
        while True:
            part = conn.recv(SIZE)
            data += part
            if len(part) < SIZE:
                break
        full_msg = data
        try:
            data = pickle.loads(full_msg[HEADERSIZE:])
        except EOFError:
            data = None
        return data


def main():
    create_connection()
    # Initialize objects
    global order_m
    ing_map = Ingredient_map()
    order_m = OrderManager()

    while True:
        msg = get_object()
        if msg is None:
            pass
        elif msg.req == Request.G_ING_MAP:  # get ingredient map
            send_object(ing_map.instance.map)
        elif msg.req == Request.P_ORDER:
            order_m.add_order(msg.data)
            print(msg.data)

    # end while
    soc.close()


if __name__ == "__main__":
    main()

服务器的输出是 订单 0 订单 1 订单 3 订单 4 订单 5 但订单 2 输了! 为什么?我应该如何解决所有订单都会到达? Request 是请求类型的枚举。

【问题讨论】:

  • 这不是一个可重现的例子。您正在调用示例中未定义或导入的类和函数。请提供一个最小的可重现示例,我们将尽力为您提供帮助。 stackoverflow.com/help/minimal-reproducible-example
  • 你可以看到github.com/Ofekroz10/Restaurant中的全部代码 class back 是服务器,class main 是客户端 其他代码不需要,无论我发送订单对象还是猫对象,都有问题是我从客户端发送 6 个对象,而服务器只得到 5 个
  • 此代码与代码in this question 惊人地相似。这些有关系吗?
  • 您能至少在一定程度上缩小问题范围吗?需要处理大量代码,不是吗?
  • @MisterMiyagi 这是什么问题?好像已经被删除了。

标签: python python-3.x sockets tcp


【解决方案1】:

让我们可视化从客户端到服务器的 6 个腌制对象:

server <- 1112222233334445555566 <- client

TCP 连接是一个数据流。没有界限。服务器必须将数据拆分为单独的部分并解开每个部分:

111 22222 3333 444 55555 66

为了能够做到这一点,您必须为数据引入一些高级结构。我无法运行您的代码,但我在那里看不到任何类似的东西。相反,服务器只是读入一个固定大小的缓冲区。现实有点复杂,但我认为这种情况有时会发生:

1112 2222 3333 4445 5555 66

物件1、3、4、6是一体转移的,但物件2、5被分成两部分,不能单独解封。

我认为您需要将腌制数据的长度放在标题中。那么服务器应该首先读取头部(固定大小),提取长度,然后读取和解压缩尽可能多的后续字节(不是更少,也不是更多)。

还有这个片段:

    try:
        data = pickle.loads(full_msg[HEADERSIZE:])
    except EOFError:
        data = None

隐藏错误。当你默默地丢弃数据时,不要责怪 TCP 丢包。

【讨论】:

  • 好的,但是我如何在标题中发送对象的长度并提取长度?你能给我看一下代码吗?谢谢
  • @ofekRozenkrantz 在 StackOverflow 我们回答问题。我不会写你要求的代码。恕我直言,与您发布的代码的复杂性相比,这是微不足道的。
【解决方案2】:

正如@VPfB 提到的,您正在发送标头大小,但从不解析大小。您确实应该重组您的代码,但是,这里有一个使用您的代码的工作示例。

在您的客户端代码中,将您的 get_object 函数更改为此。

def get_object(req):
    if soc is None:
        raise NotImplementedError  # There is no connection
    else:
        # unpickle the data
        send_object(req)
        if soc is None:
            raise NotImplementedError  # There is no connection
        else:
            # unpickle the data
            # this initial recv only receives the header length
            length = int(soc.recv(HEADERSIZE).decode('utf-8'))

            data = b''
            while True:
                # here we receive a minimum of the standard recv size 
                # and the diff between your already received data
                # and the full length, whichever is smaller
                part = soc.recv(min(SIZE, length - len(data)))
                data += part
                # if the length that was specifed in the header has been received
                # we know we have everything
                if length == len(data):
                    break
            full_msg = data
            try:
                data = pickle.loads(full_msg)
            except EOFError:
                data = None
            return data

现在我们将对您的服务器代码进行几乎相同的更改。

def get_object():
    global conn
    if conn is None:
        raise NotImplementedError  # There is no connection
    else:
        # unpickle the data
        # the only difference here is
        # when we receive nothing, we know it's time to wait for another connect
        length = conn.recv(HEADERSIZE).decode('utf-8')

        if len(length):
            length = int(length)

            data = b''
            while True:
                part = conn.recv(min(SIZE, length - len(data)))
                data += part
                if length == len(data):
                    break
            full_msg = data
            try:
                data = pickle.loads(full_msg)
            except EOFError:
                data = None
            return data
        else:
            # wait for another connection
            conn, addr = soc.accept()

实际上,服务器的行为通常是这样的:

# do this forever
while True:
    # wait for an incoming connection
    conn, addr = soc.accept()

    # get the length of the data the connection is sending
    length = int(soc.recv(HEADERSIZE).decode('utf-8'))

    # get the data
    data = b''
    while True:
        part = soc.recv(min(SIZE, length - len(data)))
        data += part

        if length == len(data):
            break

     # do some stuff with the data

不同之处在于我们在收到每个对象后等待新的连接。这也让其他客户端尝试连接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-08
    相关资源
    最近更新 更多