首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python 学习手册
Python Cookbook
Python 基础教程
Python Sites
PyPI - Python Package Index
http://www.simple-is-better.com/
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
V2EX  ›  Python

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

  •  
  •   eightqueen · 2016-04-10 17:14:04 +08:00 · 9103 次点击
    这是一个创建于 1253 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 数据,还是继续等,那要等到什么时候呢?

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

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

    就像 @mhycy 说的, TCP 是流协议不能保证你这边发送 5B 时对方会马上收到。有可能你连续发送 2 次 5B ,对方一次收到 10B 。
        10
    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 你
        11
    ryanking8215   2016-04-11 08:55:34 +08:00
    TCP 是流方式,有粘包和漏包的问题,所以一般协议里都需要同步头和数据长度等信息,让应用层来确定消息边界。
        12
    eightqueen   2016-04-11 10:36:46 +08:00
    @Mutoo 没错,就是 push ,谢谢
        13
    mengzhuo   2016-04-13 08:14:38 +08:00
    一般是 5B

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

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

    还有,如果是互联网,那么即使延迟 1s ,最终到达服务器的数据包也有可能已经被合并成 10b 的一包了。
        15
    ayiis   2016-04-16 23:46:40 +08:00
        16
    aec4d   2016-06-16 10:36:19 +08:00
    @des 你的理解是错的 当有数据可以读取的时候进行读取 最多读取 len 字节 并不是你想的一定会读到 len 字节然后才执行下一步
    你可以下载一个比较大的文件验证一下
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   774 人在线   最高记录 5043   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 23ms · UTC 18:36 · PVG 02:36 · LAX 11:36 · JFK 14:36
    ♥ Do have faith in what you're doing.