python socket 一个很简单的问题把我难住了

2016-04-10 17:14:04 +08:00
 eightqueen
import socket

response = 'HTTP/1.1 200 OK\r\nConnection: Close\r\nContent-Length: 11\r\n\r\nHello World'

server = socket.socket()
server.bind(('0.0.0.0', 9527))
server.listen(1024)

while True:
    client, clientaddr = server.accept()  # blocking
    request = client.recv(1024)  # blocking
    print request
    client.send(response)  # maybe blocking
    client.close()

这个例子很简单,运行起来也没啥问题,但是我不明白 recv 这个函数是怎么判断客户端的数据都传过来了?比如客户端应该传 10B 的数据,先传了 5B ,然后隔了 1 秒,此时 recv 应该返回收到的 5B 数据,还是继续等,那要等到什么时候呢?

11841 次点击
所在节点    Python
16 条回复
des
2016-04-10 17:29:34 +08:00
client.recv(len)
在接收到至少 len 字节的数据之前,造成一个阻塞,并暂停脚本运行( block )。但是, 如果接收到中断信号,或远程服务器断开连接,该函数将返回少于 len 字节的数据。
magicdawn
2016-04-10 17:30:08 +08:00
timeout
Mutoo
2016-04-10 17:37:04 +08:00
tcp 协议有一个 push 标记,会让传输层立即把数据传给应用层,即使缓冲区未满。
anexplore
2016-04-10 18:08:42 +08:00
可以看一下这个 http://m.blog.csdn.net/article/details?id=7689409 ,在应用层则可以通过设置数据长度或者关闭 socket 等方法表示数据接收完毕
micyng
2016-04-10 18:33:53 +08:00
如果是阻塞模式,就一直读
gamexg
2016-04-10 19:43:06 +08:00
不会等待,而是直接返回 5B 数据。
recv 意思是接受不超过 len 长度的数据。一般在阻塞模式下必须收到数据才返回,但是不在意到底收到了多少数据,也就是返回的数据可以不够 len 长度。
在阻塞模式下除非远端关闭连接(包括关闭单个方向的连接)否则必定读到数据才返回。也就是如果 recv 返回的数据是空,那么表示连接断开了。

对了上面没考虑超时的问题。
mhycy
2016-04-10 19:46:37 +08:00
如果需要长度可保证的传输请用 UDP ,那是包协议(而 TCP 是流协议)
如果需要在 TCP 做包处理,请在此基础上实现长度判断以及异常处理
(例如发生拔网线之类不会产生任何提示的网络中断)
gamexg
2016-04-10 19:47:44 +08:00
对了,还要注意 client.send ,这个函数返回已发送的字节数, python 不保证所有数据都发出去了。你需要自己检查是否都发出去了,如果没有那么需要继续通过 client.send 发送剩余的内容。可以换成 sendall 来解决。
bp0
2016-04-10 20:34:09 +08:00
通常都是在 TCP 之上增加应用层协议,先发送长度或者类似信息,然后再发送数据的。

就像 @mhycy 说的, TCP 是流协议不能保证你这边发送 5B 时对方会马上收到。有可能你连续发送 2 次 5B ,对方一次收到 10B 。
ayiis
2016-04-10 20:57:32 +08:00
如果楼主其实是想问 http 的话:
1. 读取 http 的 header
2. 解析 header
3.1 如果 Transfer-Encoding:chunked ,则需要按段读取,并把每段的段头和段尾删除
3.2 否则获取 Content-Length ,读到指定长度完毕(如果后面还有数据则截断
4. 配合超时

如果是 tcp 的话,我觉得 tcp 只有对方发送 FIN 或者 RST 之后才算结束,所以如果对方 SYN flood 你
ryanking8215
2016-04-11 08:55:34 +08:00
TCP 是流方式,有粘包和漏包的问题,所以一般协议里都需要同步头和数据长度等信息,让应用层来确定消息边界。
eightqueen
2016-04-11 10:36:46 +08:00
@Mutoo 没错,就是 push ,谢谢
mengzhuo
2016-04-13 08:14:38 +08:00
一般是 5B

@bp0 这个得看链路上有没有合包的设备,但是很少见,自己 wireshark 抓一下就知道了
bp0
2016-04-13 10:29:35 +08:00
@mengzhuo 像楼主这种局域网的情况下,中间延迟 1s ,肯定是先收到一个 5b ,然后再收到另外一个 5b 。

但是,如果中间没有延迟 1s ,客户端连续调用两次发送 5b ,在客户端的 TCP 层就会被 Nagle 算法合并了,根本不需要合包的设备。

还有,如果是互联网,那么即使延迟 1s ,最终到达服务器的数据包也有可能已经被合并成 10b 的一包了。
ayiis
2016-04-16 23:46:40 +08:00
aec4d
2016-06-16 10:36:19 +08:00
@des 你的理解是错的 当有数据可以读取的时候进行读取 最多读取 len 字节 并不是你想的一定会读到 len 字节然后才执行下一步
你可以下载一个比较大的文件验证一下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/269954

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX