为何Python asyncio异步函数未异步执行?LAN端口扫描问题
问题:局域网TCP端口扫描任务无法并行执行
我想要快速扫描局域网(LAN),找到监听特定TCP端口(例如2442,即JS8Call网络API)的主机。我参考了Python asyncio异步执行教程的程序结构,程序虽能运行,但单个任务并未并行执行,而是串行运行——任务创建和结果收集看似正常,但运行时ping异步函数的打印信息按顺序出现,总是前一个超时后才出现下一个。我猜测ping函数未生成协程对象,请问该函数存在什么问题?
代码如下:
#!/usr/bin/python import socket import asyncio socket.setdefaulttimeout(0.5) async def ping(host, port): r = f'{host}:{port} OK' try: with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect( (host, port) ) print( r ) s.close() return r except Exception as X: print(f'{host} FAILED') return X async def main(): tasks = [] async with asyncio.TaskGroup() as tg: # start all tasks for i in range(5,240): host = f'192.168.33.{i}' t = tg.create_task( ping(host, 22) ) tasks.append(t) results = [ task.result() for task in tasks ] for p in results: if p != None: print(p) asyncio.run( main() )
问题根源
你的ping函数虽被定义为async函数,但内部使用了同步阻塞的socket API——s.connect()是完全阻塞的调用,会卡住当前协程直到连接完成或超时,彻底剥夺了asyncio事件循环切换到其他协程的机会,最终导致所有任务只能串行执行。
你猜测的“ping函数未生成协程对象”是错误的:ping(host,22)确实返回了协程对象,tg.create_task也正确将其加入了任务组,问题核心出在函数内部的阻塞操作上。
解决方案
要实现真正的异步并行,必须使用asyncio提供的非阻塞网络API,替换同步的socket调用:
修改后的代码
#!/usr/bin/python import asyncio async def ping(host, port): result = f'{host}:{port} OK' try: # 使用asyncio的非阻塞TCP连接API reader, writer = await asyncio.open_connection(host, port) writer.close() await writer.wait_closed() print(result) return result except Exception as e: print(f'{host} FAILED') return e async def main(): tasks = [] async with asyncio.TaskGroup() as tg: for i in range(5, 240): host = f'192.168.33.{i}' task = tg.create_task(ping(host, 22)) tasks.append(task) # 筛选并打印成功结果 results = [task.result() for task in tasks] for res in results: if isinstance(res, str) and 'OK' in res: print(res) asyncio.run(main())
关键改动说明
- 移除同步
socket模块依赖,改用asyncio.open_connection()——这是原生支持异步非阻塞的TCP连接方法 - 用
await等待连接操作,此时事件循环会自动切换到其他就绪协程,实现真正的并行执行 - 关闭连接时使用
await writer.wait_closed(),确保异步关闭操作完成
修改后,所有扫描任务会并行执行,能大幅提升局域网端口扫描的速度。
内容的提问来源于stack exchange,提问作者Jindrich Vavruska
相关产品推荐
相关产品推荐

