【问题标题】:Asyncio close connection leave connection in TIME_WAIT state异步关闭连接使连接处于 TIME_WAIT 状态
【发布时间】:2019-03-08 12:50:21
【问题描述】:

您好,我有一个脚本可以保持我设备的端口状态,这是简化版。

当连接成功(设备存在)然后我关闭连接,连接状态变为TIME_WAIT。准时这个连接正在起球并达到操作系统允许的最大连接(如果我记得的话)

知道我应该修复哪个部分,例如,我使用端口 53,但在实际应用中,我会检查多个端口,例如 ssh、vnc 等。

我在 ubuntu 18.04 上使用 python 3.5.6 运行脚本

import asyncio
import ipaddress
import sys

async def check_port(ip, port, timeout=1):
    conn = None
    response = False
    writer = None

    try:
        conn = asyncio.open_connection(ip, port)
        reader, writer = await asyncio.wait_for(conn, timeout=timeout)
        response = True
    except asyncio.CancelledError:
        print("asyncio cancel")
    except:
        response = False
    finally:
        if writer is not None:
            writer.close()
        if conn is not None:
            conn.close()
        print("Closing connection {}:{}".format(ip, port))

    print("{}:{} {}".format(ip, port, response))

async def poll_status():
    ips = [str(ip) for ip in ipaddress.IPv4Network("192.168.1.0/24")]
    while True:
        try:
            tasks = [check_port(ip, 53) for ip in ips]
            await asyncio.wait(tasks)
        except asyncio.CancelledError:
            break
        except KeyboardInterrupt:
            break
        except:
            pass
        await asyncio.sleep(1)

async def shutdown(task):
    task.cancel()
    await task
    await asyncio.sleep(1)

if __name__ == "__main__":
    loop = asyncio.get_event_loop()
    task = asyncio.ensure_future(poll_status())
    try:
        loop.run_forever()
    except:
        pass
    finally:
        loop.run_until_complete(asyncio.wait([shutdown(task)]))
        loop.close()

连接一直像这样堆积(“netstat -nput | grep TIME_WAIT”的输出) 192.168.1.1 是我的路由器,所以它检查端口成功但留下了很多未关闭的连接。删除连接花了很长时间

tcp        0      0 192.168.1.4:42102       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:42582       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:46560       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:39428       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:45806       192.168.1.1:53          TIME_WAIT   -                                     
tcp        0      0 192.168.1.4:44752       192.168.1.1:53          TIME_WAIT   -                                      
tcp        0      0 192.168.1.4:40726       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:49864       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:38812       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:48464       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:41372       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:43408       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:47360       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:45478       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:41904       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:40160       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:46196       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:48744       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:49554       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:47774       192.168.1.1:53          TIME_WAIT   -                   
tcp        0      0 192.168.1.4:39370       192.168.1.1:53          TIME_WAIT   -                                    
tcp        0      0 192.168.1.4:43994       192.168.1.1:53          TIME_WAIT   - 

【问题讨论】:

    标签: python-3.x tcp python-asyncio time-wait


    【解决方案1】:

    我不是网络方面的专家,不确定这个答案是否有帮助,但这是我的两分钱。

    关于 netstat 输出的第一件事。它是相关的路由器,似乎与您的操作系统限制无关。快速谷歌搜索shows关注:

    TIME_WAIT 表示本地端点(这边)已经关闭了 联系。连接保持不变,因此任何延迟 数据包可以与连接匹配并进行适当处理。 当它们在四个内超时时,连接将被删除 分钟。

    似乎是您的代码关闭连接,即一切正常。

    但是我不知道路由器将如何处理越来越多的此类连接。


    现在让我们考虑一下您的代码。


    您在asyncio.wait(tasks) 行所做的是并行运行所有检查。根据ips 的数量,它可能太多了。使用asyncio.Semaphore 限制并行检查的最大数量很有可能使您受益。它会如下所示:

    sem = asyncio.Semaphore(100)
    
    async def check_port(ip, port, timeout=1):
        async with sem:
            # main code here
    

    您也可以阅读this answer 了解使用信号量的真实示例。


    接下来你可能需要解决的是你如何处理CancelledError

    except asyncio.CancelledError:
        print("asyncio cancel")
    

    这不是任务应对此异常的方式。它永远不应该压制它,只有外部代码才能做到。阅读this answer 了解如何操作。


    except:
        response = False
    

    你从不做这种事。详情请阅读this topic

    你应该至少做如下的事情:

    except Exception as exc:    # catch Exception or it's subclasses only
        logging.exception(exc)  # log for purpose not to miss exception you can fix
        response = False
    

    【讨论】:

    • 关于信号量,我在实际代码中使用它。它就像一个池,它帮助管理所有等待请求,无论是协程函数还是非协程函数(与 executor 一起运行)。这很棒。两个竖起大拇指:D
    • 对于 asyncio.CancelledError 我主要用于调试,有时它会丢失,所以我把每个协程函数都放在了测试:D,我的错。我在真实应用程序中的任何地方都记录了异常,只是为了更简单的目的在这个例子中删除它
    • 我读到了TIME_WAIT,当达到65k时它实际上会达到限制(最大源端口已用完)。在新的打开连接上,它将使用不同的源端口,然后处于 TIME_WAIT 状态,这将及时堆积。如果使用 SO_REUSEADDR 我认为它不适合我的场景,因为我必须检查同一个设备(多次相同的目标 ip 和端口)
    • 在实际应用中,我检查单个设备的多个端口和其他内容,每次检查都会打开一个连接。我正在使用无代理方法。所以达到65K是可能的。我想到的解决方案是将下一次迭代延迟到分钟( TIME_WAIT 大约在 1 分钟后被删除),但这会达到目的......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 2016-10-15
    • 1970-01-01
    • 2020-06-06
    • 1970-01-01
    相关资源
    最近更新 更多