代码: https://github.com/qiujiajin/miniweb
Pycharm 打开项目 运行 examples/example1.py 启动服务 运行 test.py ,运行测试,可以看到 sync_requests(),同步情况下调用 requests 发送请求,解析正常; 调用 async_requests(),也就是多线程处理的时候,服务端 recv 接收数据时,会丢失请求体。
请教下大佬们,讲解一下,不甚感激~
1
hsfzxjy 2021-10-30 00:43:04 +08:00 via Android 1
recv 不保证一次接收完,你要用一个循环读
|
2
yousabuk 2021-10-30 10:07:47 +08:00 via iPhone
接 1# 补充一个:
http 发的数据含 \r\n ,如果你接收按回车符判断接收完成,后边的就不读了,此时,你需要继续读就可以把后边的读出来。 |
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()在未来某一时刻也同样会带来意想不到的结果。 |
4
Lighthughjiajin OP |
5
Lighthughjiajin OP @yousabuk 我是对于客户端的每次 TCP 连接,创建一个新的 socket 去服务,那么如果我用这个 socket 根据\r\n 过滤接收,那么我多余的数据没有接收,那部分数据会怎么处理的?
而且为什么每次丢失的都是请求体~ |
6
Lighthughjiajin OP @vinle
我的实现是在一个进程里,开多个线程去处理新的 socket 连接。 但是呢,由于一个进程用一个端口号,那么所有客户端的连接发数据都是发送到这个端口号的。 我好奇每个线程里的 socket.recv ,都是到这个进程的接收缓冲区去拿的吗? |
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 |
8
Lighthughjiajin OP 我发现请求有时候是先发送请求行和首部,然后再发送请求体。
所以我先解析出请求行和首部,然后判断 Content-Length: 来判断需要目前 body 是否接收完全,如果没接收完全的话,再次调用 recv 来接收,就解决了。 |
9
muzuiget 2021-10-30 20:50:59 +08:00
|
10
julyclyde 2021-11-01 11:45:34 +08:00
|