You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AIOHTTP绑定端口遇[Errno 10048]错误,如何直接处理?

解决aiohttp重启时端口未释放的绑定错误及吞错问题

一、主动捕获绑定错误并强制终止进程

由于你的数据库框架会维持事件循环运行,aiohttp绑定端口失败后不会自动终止进程。你需要在启动逻辑中显式捕获OSError(10048)错误,直接退出整个进程:

import asyncio
import sys
from aiohttp import web

async def main():
    try:
        app = web.Application()
        # 注册路由、中间件等操作
        runner = web.AppRunner(app)
        await runner.setup()
        site = web.TCPSite(runner, '0.0.0.0', 8080)
        await site.start()
        print("Server started on 0.0.0.0:8080")
    except OSError as e:
        if e.errno == 10048:
            print(f"端口8080已被占用: {e}", file=sys.stderr)
            # 直接终止进程,避免事件循环被数据库框架维持导致服务僵死
            sys.exit(1)
        else:
            # 其他OSError正常抛出
            raise

# 数据库框架初始化逻辑
async def init_db():
    # 你的异步数据库初始化、长期运行任务逻辑
    pass

async def run_all():
    await init_db()
    await main()

if __name__ == "__main__":
    asyncio.run(run_all())

二、设置Socket重用选项

通过自定义Socket并开启SO_REUSEADDR(Windows/Linux通用)和SO_REUSEPORT(Linux/macOS支持)选项,让端口可以被快速重用,减少端口未释放导致的绑定失败:

import asyncio
import socket
from aiohttp import web

async def main():
    app = web.Application()
    # 注册路由等操作

    # 创建自定义Socket并设置重用参数
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # Windows下SO_REUSEPORT可能不支持,做兼容处理
    try:
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
    except AttributeError:
        pass
    sock.bind(('0.0.0.0', 8080))

    runner = web.AppRunner(app)
    await runner.setup()
    # 使用自定义Socket启动站点
    site = web.SockSite(runner, sock)
    await site.start()
    print("Server started on 0.0.0.0:8080")

# 数据库初始化和run_all函数同上

三、启动前检测端口占用状态

在启动aiohttp之前先检测端口是否被占用,提前终止进程避免进入僵死状态:

import socket
import sys

def is_port_occupied(port):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        # 尝试连接端口,返回0表示已被占用
        return s.connect_ex(('0.0.0.0', port)) == 0

async def main():
    if is_port_occupied(8080):
        print("端口8080已被占用,退出进程", file=sys.stderr)
        sys.exit(1)
    # 后续aiohttp启动逻辑

四、统一管理事件循环,清理后台任务

如果数据库框架有长期运行的异步任务,在aiohttp启动失败时主动取消这些任务,让事件循环可以正常退出:

import asyncio
import sys
from aiohttp import web

db_background_task = None

async def long_running_db_task():
    # 数据库后台长期运行任务,比如监听变更
    while True:
        await asyncio.sleep(3600)

async def init_db():
    global db_background_task
    db_background_task = asyncio.create_task(long_running_db_task())
    await db_background_task

async def main():
    try:
        app = web.Application()
        # 注册路由等操作
        runner = web.AppRunner(app)
        await runner.setup()
        site = web.TCPSite(runner, '0.0.0.0', 8080)
        await site.start()
        print("Server started on 0.0.0.0:8080")
    except OSError as e:
        if e.errno == 10048:
            print(f"端口8080被占用: {e}", file=sys.stderr)
            # 取消数据库后台任务,让事件循环可以退出
            if db_background_task:
                db_background_task.cancel()
                try:
                    await db_background_task
                except asyncio.CancelledError:
                    pass
            sys.exit(1)
        else:
            raise

async def run_all():
    await init_db()
    await main()

if __name__ == "__main__":
    try:
        asyncio.run(run_all())
    except asyncio.CancelledError:
        pass

内容的提问来源于stack exchange,提问作者Jthorpe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:22:57