自定义 Socket 接收 HTTP 请求,丢失请求体 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Lighthughjiajin
V2EX    Python

自定义 Socket 接收 HTTP 请求,丢失请求体

  •  
  •   Lighthughjiajin 2021-10-29 23:47:00 +08:00 2711 次点击
    这是一个创建于 1446 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码: https://github.com/qiujiajin/miniweb

    Pycharm 打开项目 运行 examples/example1.py 启动服务 运行 test.py ,运行测试,可以看到 sync_requests(),同步情况下调用 requests 发送请求,解析正常; 调用 async_requests(),也就是多线程处理的时候,服务端 recv 接收数据时,会丢失请求体。

    请教下大佬们,讲解一下,不甚感激~

    10 条回复    2021-11-01 11:45:34 +08:00
    hsfzxjy
        1
    hsfzxjy  
       2021-10-30 00:43:04 +08:00 via Android   1
    recv 不保证一次接收完,你要用一个循环读
    yousabuk
        2
    yousabuk  
       2021-10-30 10:07:47 +08:00 via iPhone
    接 1# 补充一个:

    http 发的数据含 \r\n ,如果你接收按回车符判断接收完成,后边的就不读了,此时,你需要继续读就可以把后边的读出来。
    1BF6oSYCD9ngBHo1
        3
    1BF6oSYCD9ngBHo1  
       2021-10-30 10:35:42 +08:00
    前提条件:一般来说 server 都是一个请求对应一个进程。

    1 )单线程的时候,客户端的全部 request 都是 blocking 的,即,每一个 request 的发送都基于上一个的 request-response 流程的成功完成,所以如果**服务器的处理逻辑正确**的情况下,你发了多少,server 就收到多少。

    2 )对于楼主多线程的实现,在某一时刻 T1 ,可能出现这种情况:客户端所在的设备的网络接口( i.e. 网卡)会把 N 个 request 一起送出去,N>=1 。根据上述的前提条件,楼主的服务端(本质就是个 httpserver )在下一时刻 T2 ,会对进来的流量进行处理(即,T1 时刻发的流量),然而同样,所以如果**服务器的处理逻辑正确**的情况下,server 会识别出有 N 个 req ,然后分别处理,所以有同样的结果:你发了多少,server 就收到多少。

    综上,楼主说的 issue 和客户端线程是没关系的(必须的,不然电商怎么玩抢购),那问题实在哪里?就是上面写的正确的服务器处理逻辑。 很可惜的是这东西并不能靠改楼主的某一行代码就能解决问题(其实就是__init__.py, 144 ,加上楼上朋友的建议),因为正确的处理逻辑基于正确的了解“网络的属性”,如果没有后者,楼主的 WebServer()在未来某一时刻也同样会带来意想不到的结果。
    Lighthughjiajin
        4
    Lighthughjiajin  
    OP
       2021-10-30 11:16:10 +08:00
    Lighthughjiajin
        5
    Lighthughjiajin  
    OP
       2021-10-30 11:23:09 +08:00
    @yousabuk 我是对于客户端的每次 TCP 连接,创建一个新的 socket 去服务,那么如果我用这个 socket 根据\r\n 过滤接收,那么我多余的数据没有接收,那部分数据会怎么处理的?
    而且为什么每次丢失的都是请求体~
    Lighthughjiajin
        6
    Lighthughjiajin  
    OP
       2021-10-30 11:38:20 +08:00
    @vinle
    我的实现是在一个进程里,开多个线程去处理新的 socket 连接。
    但是呢,由于一个进程用一个端口号,那么所有客户端的连接发数据都是发送到这个端口号的。
    我好奇每个线程里的 socket.recv ,都是到这个进程的接收缓冲区去拿的吗?
    1BF6oSYCD9ngBHo1
        7
    1BF6oSYCD9ngBHo1  
       2021-10-30 14:28:29 +08:00
    @Lighthughjiajin 刚刚我的描述可能有些偏差,纠正一下,前提条件那应该是“最基础的 server”。

    “...发送到这个端口号的....” 即使你 N 个请求一起发送到给定的端口号,数据也不是聚在一起的,所以不存在单一的“进程的接收缓冲区”。事实是,一个 socket 对应一个连接,你指的 N 个线程里的 socket.recv ,那便是到这个 N 个连接对应的 N 个 socket 的对应的 connection 的所对应的缓冲区里边去拿。有点绕,其实就是 socket-conn-buffer 一一对应的意思。

    Ref. https://en.wikipedia.org/wiki/Berkeley_sockets#accept
    Lighthughjiajin
        8
    Lighthughjiajin  
    OP
       2021-10-30 14:49:14 +08:00
    我发现请求有时候是先发送请求行和首部,然后再发送请求体。
    所以我先解析出请求行和首部,然后判断 Content-Length: 来判断需要目前 body 是否接收完全,如果没接收完全的话,再次调用 recv 来接收,就解决了。
    muzuiget
        9
    muzuiget  
       2021-10-30 20:50:59 +08:00
    @Lighthughjiajin HTTP 的那个 Content-Length 本来的作用就是如此,你这个做法就是对的。

    至少目前楼主没有说出两字禁言。
    julyclyde
        10
    julyclyde  
       2021-11-01 11:45:34 +08:00
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     927 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 20:52 PVG 04:52 LAX 13:52 JFK 16:52
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86