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
just1
V2EX  ›  Python

Python socket 通信如何分包

  •  
  •   just1 · 2017-04-01 21:48:37 +08:00 · 6458 次点击
    这是一个创建于 2794 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如服务端发送 00 01 ; 00 02 两个包 客户端用 python socket.recv()的函数会接收到 00 01 00 02 (粘连在一起) 如何分包? 服务端我无法控制。 客户端用 python3

    之前发在 segmentfault 没人理。。。 https://segmentfault.com/q/1010000008888222

    31 条回复    2017-04-03 15:58:22 +08:00
    wevsty
        1
    wevsty  
       2017-04-01 22:01:44 +08:00
    发一个,等几秒再发第二个
    zyEros
        2
    zyEros  
       2017-04-01 22:01:49 +08:00 via iPhone
    尾部特殊字符,或者头部用 2 字节表明 body 的长度
    just1
        3
    just1  
    OP
       2017-04-01 22:02:30 +08:00 via Android
    @wevsty
    @zyEros 服务端不可控
    wevsty
        4
    wevsty  
       2017-04-01 22:04:39 +08:00
    @just1 那就自己按照包大小或者包格式分割一下,如果没有固定格式,弃坑吧
    zyEros
        5
    zyEros  
       2017-04-01 22:05:29 +08:00 via iPhone
    @just1 服务端都不能标明你 body 的长度或者截止位置,你能怎么办,没办法
    dant
        6
    dant  
       2017-04-01 22:16:48 +08:00
    TCP 是数据流。
    just1
        7
    just1  
    OP
       2017-04-01 22:18:49 +08:00
    @zyEros
    @wevsty
    @dant TAT 那 wireshark 怎么实现分包的
    maemual
        8
    maemual  
       2017-04-01 22:24:21 +08:00
    tcp 流式数据你是分不了包的。只能加结束符或者发送 content length 之类的东西来判断。
    ifaii
        9
    ifaii  
       2017-04-01 22:41:50 +08:00 via iPhone
    tcp 是流,这种需求不如试试 udp
    lvxudong
        10
    lvxudong  
       2017-04-01 22:49:43 +08:00
    socket.recv(num) 可以指定接收字节数
    wevsty
        11
    wevsty  
       2017-04-01 23:06:57 +08:00   ❤️ 1
    @just1 抓包工具抓的并不只是 tcp 层面的数据。一个 tcp 包除了发送或者接收的数据还包括了很多内容,比如目标端口,原端口,也有些包不包含应用层的数据。和你理解的接收包不是一回事。解释起来很复杂,但是在应用层的层面上 TCP 应用层的应用是分不清楚包的。如果一定要按照包来处理内容,请使用 UDP 。
    当然,使用 UDP 你肯定会遇到更多麻烦。
    mooncakejs
        12
    mooncakejs  
       2017-04-01 23:17:20 +08:00 via iPhone
    tcp 不需要分包。按照应用层协议解析就好
    jimzhong
        13
    jimzhong  
       2017-04-01 23:18:32 +08:00
    如果你是 Linux 的 UNIX Socket ,可以试试 SEQPACKET 协议。
    如果是 TCP ,那么建议自己设计一个头,里面包含数据长度,就像 HTTP 的 Content-length 一样
    jimzhong
        14
    jimzhong  
       2017-04-01 23:20:21 +08:00   ❤️ 1
    @just1 TCP 在传递到 IP 层的时候会分成一个个 Segment , Segment 的大小由 MSS 决定。每个 Segment 被封装到一个 IP 包里面。但是 Segment 对于应用程序是不可见的,应用程序看到的就是流。
    just1
        15
    just1  
    OP
       2017-04-01 23:25:36 +08:00 via Android
    @maemual
    @ifaii
    @lvxudong
    @mooncakejs (´・_・`)模拟某个客户端的啦,我再想办法找找他的处理模式

    @wevsty @jimzhong 。◕‿◕。感谢解惑
    raiz
        16
    raiz  
       2017-04-01 23:26:42 +08:00
    既然是tcp,本身是流,肯定要有数据协议,就是根据数据包格式,起始标识符,长度域或结束符来判断完整的一包.具体就是逐字节的读取,判断,直到一完整包,给上层,继续读取判断.
    pright
        17
    pright  
       2017-04-02 00:50:50 +08:00   ❤️ 1
    如果每个包长度都小于 MSS ,你也可以用 raw socket 自己收 233
    best1a
        18
    best1a  
       2017-04-02 01:40:28 +08:00
    @pright 即使每个包长度都小于 MSS ,如果服务端开启了 nagle ,一样会有这问题吧
    NoAnyLove
        19
    NoAnyLove  
       2017-04-02 04:26:18 +08:00   ❤️ 1
    TCP 是面向数据流的啊,你可以自己定义一个应用层的格式,比如每个包头包含这个包的大小。 recv 的时候先接收包头大小的内容,然后再根据包头接受剩下的数据。当你用直接用 TCP 协议的时候,就不用考虑下面的分包问题了。除非你想用 UDP
    ryd994
        20
    ryd994  
       2017-04-02 07:39:42 +08:00 via Android
    1.每个包以长度开头
    2.用库或者自己再实现一层 buffer ,用 deque 实现不难的
    3.SCTP
    ryd994
        21
    ryd994  
       2017-04-02 07:44:40 +08:00 via Android   ❤️ 1
    关了 nagle ,每次 flush ,接收端读取频率够高的话,应该不会粘
    但是不保证,毕竟
    1.操作系统可以有其他实现
    2.拥塞控制 /流控挡了一下,剩下的在发送缓冲合并了
    3.接收乱序,等到齐了的时候一起进接收缓冲
    4.服务端程序被其他事件打断,睡了一会
    huxh10
        22
    huxh10  
       2017-04-02 08:26:58 +08:00   ❤️ 1
    TCP 确实是一个包一个包发的, wireshark 也能看到。
    编程用的 API 是 socket , socket 是在 TCP 和 UDP 之上又提供了一层抽象。用 socket 处理 SOCK_STREAM ,是流式数据,需要自己再定义包头校验。
    shuax
        23
    shuax  
       2017-04-02 09:02:18 +08:00 via Android   ❤️ 1
    我们一般用 tlv , type , length , value ,其中 tl 大小固定,叫做包头, value 变长,由 length 决定。先只收包头,收到以后再根据长度收取 value 。
    falseen
        24
    falseen  
       2017-04-02 09:04:27 +08:00 via Android   ❤️ 1
    用常规方法实现不了,或许可以试试非常规方法。比如用 scapy 抓取 ip 包,然后从中拿到数据。
    magicdawn
        25
    magicdawn  
       2017-04-02 09:47:49 +08:00
    什么 content-length, 换行符等特殊字符啊都是简单的应用层协议啊
    aabbccli
        26
    aabbccli  
       2017-04-02 11:18:37 +08:00
    就像 HTTP 服务器一样,有三种方式:
    一 写完数据, flush 一下缓存
    二 用 CONTENT-LENGTH 来标明 PAYLOAD 的大小
    三 用类似于 HTTP1.1 的 TRANSFER-ENCODING 的格式来分块
    aploium
        27
    aploium  
       2017-04-02 11:22:07 +08:00
    1. 最简单的是强制规定每个包都是固定长度, 比如 64bytes 每次 .recv(64) 就是一个包

    2. 稍微复杂一点, 用一小段序列作为包之间的分隔两个包,比如 b'\xff\xee\xcc\xaa\xbb\xdd\x00'
    收到的东西先存到 buffer 里, 然后根据这段序列自己分隔
    优点是很简单, 缺点是可能会误分隔, 以及安全问题. 需要自己处理转义

    3. 更复杂的是自己设计一个简单的协议(参考 HTTP) 在协议头部标明内容的长度等一些元信息, 接收端 buffer 后进行分割, 不容易出错

    不知道有没有现成的库能做这种事情, 有的话请[at]我
    ryd994
        28
    ryd994  
       2017-04-02 11:35:27 +08:00
    @aploium SCTP over udp?
    ihuotui
        29
    ihuotui  
       2017-04-02 14:30:41 +08:00 via iPhone
    自己定协议 然后自己分包,实现参考 netty
    sheep3
        30
    sheep3  
       2017-04-02 18:24:31 +08:00
    早在在 segmentfault 看到你的问题了。。。。。 没有做过所以也没法回答 orz
    julyclyde
        31
    julyclyde  
       2017-04-03 15:58:22 +08:00
    首先要 坚定 TCP 是流 的信念
    然后才能想到如何在流里区分数据的开始和结束
    而不是幻想着把应用层的开始结束标志和 TCP segment 对应起来
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3718 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 04:21 · PVG 12:21 · LAX 20:21 · JFK 23:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.