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 · 2381 次点击
    这是一个创建于 1121 天前的主题,其中的信息可能已经有所发展或是发生改变。

    代码: 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 ,如果你接收按回车符判断接收完成,后边的就不读了,此时,你需要继续读就可以把后边的读出来。
    vinle
        3
    vinle  
       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 ,都是到这个进程的接收缓冲区去拿的吗?
    vinle
        7
    vinle  
       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   ·   实用小工具   ·   2594 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 15:33 · PVG 23:33 · LAX 07:33 · JFK 10:33
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.