V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
drzhaug
V2EX  ›  程序员

有关 tcp 和 http 的问题

  •  
  •   drzhaug · 2017-12-01 13:20:45 +08:00 · 5355 次点击
    这是一个创建于 2346 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我在思考一个问题,希望各位能给我解答下。 有没有可能一个 http 的 response 数据过长,超过了单个 tcp 的 payload,然后被分割在多个 tcp 报文中? 如果是这样的话,假设被分割到两个 tcp 报文当中了,如何识别第二个报文的 payload 是 http 数据?(因为可能无法通过 HTTP / 1.1 识别了)

    45 条回复    2017-12-02 17:20:25 +08:00
    a7a2
        1
    a7a2  
       2017-12-01 13:23:17 +08:00
    你这个识别在 tcp 层面完成。。。
    drzhaug
        2
    drzhaug  
    OP
       2017-12-01 13:24:40 +08:00
    @a7a2 我知道啊,我问的是 how
    exch4nge
        3
    exch4nge  
       2017-12-01 13:25:05 +08:00
    TCP 好像没有当个报文之类的一说吧,TCP 是个流,TCP 层下面的 IP 层会处理分包吧……
    wcsjtu
        4
    wcsjtu  
       2017-12-01 13:25:21 +08:00
    这。。。。。
    content-length 啊, 就算是 chunk 编码,也有终止符号。 应用层不用关心传输层的东西,不用管它分不分包
    drzhaug
        5
    drzhaug  
    OP
       2017-12-01 13:26:15 +08:00
    @exch4nge 老哥你错了
    momocraft
        6
    momocraft  
       2017-12-01 13:28:26 +08:00
    1. 有
    2. 从 tcp 读出来的只是字节流,看不到分几个包来的
    3dwelcome
        7
    3dwelcome  
       2017-12-01 13:28:42 +08:00
    wireshark 里,tcp 分包识别,是通过包的序列号。比如第一个是 http,那么后边的 TCP 流就都是 http 协议的子包。

    只通过中间一个 payload 识别格式,那是不可能的。
    drzhaug
        8
    drzhaug  
    OP
       2017-12-01 13:29:09 +08:00
    @wcsjtu 你这么说我就懂了,我之前不知道怎么处理 chunk 编码的数据。 我试着用 libpcap 抓 http 包。不是在写应用层哈。。
    drzhaug
        9
    drzhaug  
    OP
       2017-12-01 13:30:26 +08:00
    @3dwelcome 你说的我懂,我的意思是 tcp 按序列号排序后,如何在流中捕获其中的 http payload
    rrfeng
        10
    rrfeng  
       2017-12-01 13:32:49 +08:00 via Android
    TCP 分片、合并都在 TCP 层完成。

    HTTP 不需要关心底层如何实现。HTTP 看到的就是完整的数据
    3dwelcome
        11
    3dwelcome  
       2017-12-01 13:34:28 +08:00
    你单独捕获中间一个 payload 是没用的。

    一般 HTTP 服务器都有 GZIP 压缩功能,TCP/HTTP 流只有从头到尾按序列号,把每个包组合到一起,才能解压获取完整内容。
    cabing
        12
    cabing  
       2017-12-01 13:35:40 +08:00
    这个就涉及到 tcp 的 TCP 粘包和拆包问题,要说的很多啊,你百度下就行 ^^
    xiaxiaocao
        13
    xiaxiaocao  
       2017-12-01 13:37:53 +08:00
    你要写的是不是像我这个项目的东西:
    https://github.com/hsiafan/httpparse
    drzhaug
        14
    drzhaug  
    OP
       2017-12-01 13:38:36 +08:00
    @3dwelcome 我已经知道怎么做了,谢谢。
    Mutoo
        15
    Mutoo  
       2017-12-01 13:47:03 +08:00
    wireshark 有一个 follow the tcp stream 的功能,是根据 ack 编号. TCP 严格按照顺序,所以没有其它需要顾虑的。
    zhujinliang
        16
    zhujinliang  
       2017-12-01 13:50:09 +08:00
    一般防火墙或网监只做包识别,要是流的识别需要跟踪每个 TCP 连接,甚至需要缓存一段数据,开销太大
    所以有的就故意在特征处断开,绕过检测
    操作系统的话,由协议栈处理,每个连接都有对应的缓冲区,每个数据包到达后,由协议栈识别 IP、端口等,写入对应的缓冲区,对于程序来说,从缓冲区里读出来的是连续的
    drzhaug
        17
    drzhaug  
    OP
       2017-12-01 13:58:17 +08:00
    @xiaxiaocao 我大概看了下,你的意思是抓取 http request 的 tcp 报文,然后模拟 tcp 请求,然后再处理返回的 response ?
    ryd994
        18
    ryd994  
       2017-12-01 13:59:55 +08:00
    四元组
    drzhaug
        19
    drzhaug  
    OP
       2017-12-01 14:00:21 +08:00
    @zhujinliang 没错,我尝试写一个抓取 http 的程序,就是在模拟一个协议栈,实验性的哈。
    zhangysh1995
        20
    zhangysh1995  
       2017-12-01 14:10:52 +08:00
    计算机网络没学好吧,网络层才分段的,Internet 的网络层就是 IP,这里才会把上层打包的内容分段。。。。
    drzhaug
        21
    drzhaug  
    OP
       2017-12-01 14:15:44 +08:00
    @zhangysh1995 你没理解我说的意思 你说的东西太简单了
    drzhaug
        22
    drzhaug  
    OP
       2017-12-01 14:18:07 +08:00
    @xiaxiaocao 谢谢,我刚刚没有太理解你的代码,我又看了一下,应该是我要的东西,我是想在 http 的基础上把图片截取出来。ps:我才知道 pcap 有 go 版本的,果断准备放弃 c 了。
    zhangysh1995
        23
    zhangysh1995  
       2017-12-01 14:20:43 +08:00
    @zhangysh1995 因为 IP 分段之后,到大主机会重新打包的。头部有 checksum 或者 length 这种参数,底层就处理了。不管里面存的是什么,是要长度不够就会拼起来。具体实现你要去看协议栈了,因为 HTTP 不管这些事情,它只管丢给传输层。建议重新看下计算机网络。
    clino
        24
    clino  
       2017-12-01 14:25:40 +08:00
    @drzhaug #9 你标题的问题说和 tcp 相关,然后你这里问的问题又变成和 tcp 无关
    clino
        25
    clino  
       2017-12-01 14:27:46 +08:00
    建议楼主用 wireshark 抓个包然后看看协议分析以后的数据,估计能比较明了,ip/tcp/http 这几个层级的数据解析都能看到
    drzhaug
        26
    drzhaug  
    OP
       2017-12-01 14:30:13 +08:00
    @zhangysh1995 好吧,我确实应该再看一下。但你能不能先告我一下我应该怎么做:我捕获的流过路由器上的 ethernet 帧,因该怎么处理才能获得完整的 http 响应数据的 body ?大概说一下就行,能理解的。谢谢
    easing
        27
    easing  
       2017-12-01 14:33:29 +08:00
    用你问题里的例子,单纯看第二个 tcp 包是看不出来是不是 http payload 的,因为可能没有 http response header 字段供你判断,对 TCP 来说,它感知不了也不应该感知这是什么,那如何判断呢?只能在上层即 http 里,因为之前接收到了 response header,且有 content-length 信息,就会等待这么长的数据传上来,不管等多少个包,只要等够这么长了, 就算这个 http response 结束。
    hcnhcn012
        28
    hcnhcn012  
       2017-12-01 14:39:17 +08:00 via iPhone
    这个你不用担心,tcp 中的 ack 和 seq 字段一定会保证 tcp 数据段的有序的,超过了 MSS 就会拆包,不管里面包着的 http 怎么样,所以说这是面向 stream,而且 tcp 数据段不像 ip 数据包一样里面有个协议字段,也就是说 tcp 从来不知道数据部分封装的是个什么东西,也就是说在 tcp 层面无法识别 http
    zhangysh1995
        29
    zhangysh1995  
       2017-12-01 14:45:08 +08:00
    @drzhaug 我们一般的操作都是在自己的主机上进行的,用 Wireshark 可以完成,它也支持交换机或者路由器,可以参考这里 https://wiki.wireshark.org/CaptureSetup/Ethernet (全英文)。不过我觉得应该有更好的解决方案,这个就超出我的知识范围了,不好意思。
    xiaxiaocao
        30
    xiaxiaocao  
       2017-12-01 14:49:09 +08:00   ❤️ 1
    @drzhaug 你这么说就明白多了,和我那个项目应该是同样功能。
    主要是把 tcp 连接上的包重新组织好,然后就可以当作两个流来处理了,是不是 http 就通过开头的的内容可以判断。一个 http 内容读完之后如果下面的不是新的合法的 http 的 header 就放弃,当然这种情况基本不会出现。
    wcsjtu
        31
    wcsjtu  
       2017-12-01 14:57:01 +08:00
    @drzhaug 你这是要自己实现个 tcp/ip 协议栈啊
    gejigeji
        32
    gejigeji  
       2017-12-01 15:00:53 +08:00   ❤️ 3
    所以应届生校招经常问七层网络协议的问题
    neoblackcap
        33
    neoblackcap  
       2017-12-01 15:07:36 +08:00
    你就是要自己进行抓包对吧,在不用 libpcap 的情况下,那么说到底就是自己在用户空间重新实现 tcp/ip 协议栈,你具体可以参考 https://github.com/F-Stack/f-stack。
    等你完成这个之后你就得到了一个你自己实现的 stock api,剩下的就是一般 http parser 的问题了,反正你得到了完整的 tcp 流了
    Chingim
        34
    Chingim  
       2017-12-01 16:07:16 +08:00 via Android
    tcp 根本就不关心你 payload 不 payload。应用层给他什么他就传什么,如果包太大,它就拆了再传,到了目的地再把拆的包封好然后发给应用层。
    如果你想了解如何拆装,那看 tcp 就好。
    iceheart
        35
    iceheart  
       2017-12-01 19:09:30 +08:00 via Android
    看上去干的不是什么好事
    cwek
        36
    cwek  
       2017-12-01 21:26:35 +08:00
    TCP 会自动帮你拼合成连续的字节流送上应用层的,应用层感知不到。
    drzhaug
        37
    drzhaug  
    OP
       2017-12-02 00:38:27 +08:00
    @iceheart 哈哈哈,就你精,我学安全的。。。
    luxin88
        38
    luxin88  
       2017-12-02 00:55:35 +08:00
    @cabing 对于单个请求来讲,应该不会涉及到粘包的问题,只有连续发送,才要考虑粘包
    dangyuluo
        39
    dangyuluo  
       2017-12-02 01:11:46 +08:00
    楼上说的很明白了,tcp 不管你的应用,你传多长的都行。他会根据自己的协议进行分包、合并、请求重发等。
    sinxccc
        40
    sinxccc  
       2017-12-02 02:21:58 +08:00
    @gejigeji 不管应届社招,不管什么级别,问问从浏览器里敲进去网址到整个网页显示完这段时间发生了什么总没错的ˊ_>ˋ
    msg7086
        41
    msg7086  
       2017-12-02 03:40:42 +08:00
    先搞明白网络分层问题。

    物理层 绳子
    链路层 以太网
    网络层 IP
    传输层 TCP
    应用层 HTTP

    上层是 HTTP 数据,到了下层 TCP 的时候,几乎一定是会拆包的。
    通常 TCP 包不到 2KB,你有多少 HTTP 流量是前后加起来少于 2KB 的。

    如果你能拿到所有的 TCP 豹纹,那就按照 TCP 协议拆包解码成流就行了。
    根据 IP 和端口找到对应的 TCP Session,排好序以后拆包得到完整的流就行了。
    如果只能偶尔拿到一两个包,那是没法直接判断 HTTP 请求的,最多靠猜。
    cabing
        42
    cabing  
       2017-12-02 09:07:35 +08:00
    @luxin88 恩。
    fivestrong
        43
    fivestrong  
       2017-12-02 09:54:19 +08:00 via Android
    go 语言有个 google 提供的 gopacket 包,里面有一些 tcp 流重装的例子,我之前也想抓包获取 http 请求的内容,可惜学艺不精,只能找到 ip 端口,再往下就不知道怎么解析 http 数据了。
    cy18
        44
    cy18  
       2017-12-02 10:19:02 +08:00 via Android
    @zhujinliang 现在 sni 代理好像被认证了,没法用。能不能用这个思路在适当的地方把 TCP 包截断从而避开 sni 被认证的问题?感觉大墙可能还没有做到跟踪每一个 TCP 链接。
    q397064399
        45
    q397064399  
       2017-12-02 17:20:25 +08:00
    流..
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   769 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 22:31 · PVG 06:31 · LAX 15:31 · JFK 18:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.