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

Python websockets模块搭配IIS反向代理连接异常问题排查

问题:IIS反向代理Python websockets服务器连接异常断开

环境与配置

Python WebSocket服务器代码

运行在localhost:5005,代码来自websockets官方文档:

#!/usr/bin/env python

import asyncio
import websockets
import logging

logger = logging.getLogger('websockets')
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())

async def echo(websocket):
    async for message in websocket:
        print(message)
        await websocket.send(message)

async def main():
    async with websockets.serve(echo, "localhost", 5005):
        await asyncio.Future()  # run forever

asyncio.run(main())

IIS 10反向代理配置

目标是让客户端通过ws://localhost/app访问Python服务器,配置步骤:

  • 应用程序请求路由缓存 → 服务器代理设置 → 启用代理
  • 站点 → 默认网站:添加名为app的应用程序
  • URL重写规则配置:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="ReverseProxy" stopProcessing="true">
                    <match url="(.*)" />
                    <action type="Rewrite" url="http://localhost:5005" logRewrittenUrl="true" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

问题现象

客户端尝试连接ws://localhost/app时立即断开,Python服务器抛出以下异常:

= connection is CONNECTING
< GET / HTTP/1.1
< Cache-Control: no-cache
< Connection: Upgrade
< Pragma: no-cache
< Upgrade: websocket
< Accept-Encoding: gzip, deflate, br, peerdist
< Accept-Language: en-US,en;q=0.9
< Host: localhost:5005
< Max-Forwards: 10
< User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36
< Origin: null
< Sec-WebSocket-Version: 13
< Sec-WebSocket-Key: /C9ah/z+wAo4mnoACSz2IA==
< Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
< X-Original-URL: /app
< X-Forwarded-For: [::1]:52798
< X-ARR-LOG-ID: 8c59749a-4e5d-43cf-8a5a-c4421cc4ecc8
< X-P2P-PeerDist: Version=1.1
< X-P2P-PeerDistEx: MinContentInformation=1.0, MaxContentInformation=2.0
> HTTP/1.1 101 Switching Protocols
> Upgrade: websocket
> Connection: Upgrade
> Sec-WebSocket-Accept: W9Sttf7fHX3aB9zd4b/PVGt0Ldg=
> Sec-WebSocket-Extensions: permessage-deflate; server_max_window_bits=12; client_max_window_bits=12
> Date: Fri, 15 Jul 2022 19:14:32 GMT
> Server: Python/3.7 websockets/10.3
connection open
= connection is OPEN
! failing connection with code 1006
= connection is CLOSED
x half-closing TCP connection
connection handler failed
Traceback (most recent call last):
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 945, in transfer_data
    message = await self.read_message()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1015, in read_message
    frame = await self.read_data_frame(max_size=self.max_size)
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1090, in read_data_frame
    frame = await self.read_frame(max_size)
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 1149, in read_frame
    extensions=self.extensions,
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\framing.py", line 70, in read
    data = await reader(2)
  File "C:\Program Files\Python37\lib\asyncio\streams.py", line 677, in readexactly
    raise IncompleteReadError(incomplete, n)
asyncio.streams.IncompleteReadError: 0 bytes read on a total of 2 expected bytes
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\server.py", line 232, in handler
    await self.ws_handler(self)
  File "D:/Robotic Framework/RoboticApps/UI_Framework/server.py", line 12, in echo
    async for message in websocket:
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 482, in __aiter__
    yield await self.recv()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 553, in recv
    await self.ensure_open()
  File "C:\Users\amin.ramezanifar\AppData\Roaming\Python\Python37\site-packages\websockets\legacy\protocol.py", line 921, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: no close frame received or sent
connection closed

验证情况

  • 使用simple-websocket-server库搭建的WebSocket服务器,通过IIS代理可正常连接
  • 简单Node.js WebSocket服务器,通过IIS代理也可正常运行
    排除IIS代理的基础配置问题,问题出在Python websockets库与IIS的交互上。

问题原因与解决方法

原因分析

客户端请求携带了Accept-Encoding头和permessage-deflate扩展,IIS默认开启的内容压缩功能会在WebSocket连接建立后,尝试对帧数据进行压缩,破坏了WebSocket的帧结构,导致Python websockets库无法正确解析帧,最终引发连接断开。

解决方法

方法1:关闭IIS站点的压缩功能

  1. 打开IIS管理器,找到目标站点
  2. 双击"压缩"功能
  3. 取消勾选"启用静态内容压缩"和"启用动态内容压缩"
  4. 保存配置并重启站点

方法2:修改URL重写规则,禁用压缩转发

修改URL重写的XML配置,添加WebSocket请求条件,并清空Accept-Encoding头,避免IIS对WebSocket数据进行压缩:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
                <rule name="ReverseProxy" stopProcessing="true">
                    <match url="(.*)" />
                    <conditions>
                        <add input="{HTTP_UPGRADE}" pattern="websocket" />
                        <add input="{HTTP_CONNECTION}" pattern="upgrade" />
                    </conditions>
                    <action type="Rewrite" url="http://localhost:5005/{R:1}" logRewrittenUrl="true" />
                    <serverVariables>
                        <set name="HTTP_X_ORIGINAL_ACCEPT_ENCODING" value="{HTTP_ACCEPT_ENCODING}" />
                        <set name="HTTP_ACCEPT_ENCODING" value="" />
                    </serverVariables>
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>
  • 添加条件确保仅转发WebSocket升级请求
  • 清空Accept-Encoding头,阻止IIS对WebSocket数据进行压缩
  • 重写URL时保留原始路径({R:1}),避免路径不一致问题

方法3:禁用websockets库的permessage-deflate扩展

修改Python服务器代码,创建服务器时禁用该扩展:

async def main():
    async with websockets.serve(
        echo, 
        "localhost", 
        5005,
        extensions=[]  # 禁用所有扩展,包括permessage-deflate
    ):
        await asyncio.Future()  # run forever

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 22:39:25