最近在学习流量录制框架goreplay(GitHub - buger/goreplay: GoReplay is an open-source tool for capturing and replaying live HTTP traffic into a test environment in order to continuously test your system with real data. It can be used to increase confidence in code deployments, configuration changes and infrastructure changes.),发现需要了解TCP协议,不然读不懂相关的代码。因此开始了TCP 相关的学习,TCP/IP 协议本身是比较大的内容,这里核心关注报文结构和一次应用层请求引起的tcp交互。这里先思考一下2个问题:

  1. 首先为什么要有协议?
  2. 协议为什么要对报文格式有约定?

基本概念:

  • 应用层(HTTP)的分组称为报文(message)
  • 传输层(TCP)的分组称为报文段 (segment)
  • 网络层的分组称为数据报 (datagram);

socket这个英⽂单词的原意是“插⼝”“插槽”, 在⽹络编程中,它的寓意是可以通过插⼝接⼊的 ⽅式,快速完成⽹络连接和数据收发。可以理解为操作系统提供的网络插槽。

套接字:UDP是二元祖(目的IP,目的端口);TCP是四元祖(源IP,源端口,目的IP,目的端口)

TCP/IP报文解析

一个HTTP包(应用层)通过TCP协议(传输层)封装,接着通过IP协议(网络层)承载,而IP报文又通过以太网(数据链路层)传输。所以从网卡抓取的包是一个完整的4层协议的包,是一层层套娃的结构。这里结合《趣谈网络协议》里的一张图理解下

也就是说一个完整的在网上跑的包都是有这4层的,可以有下层没上层(比如tcp的握手包没有http层的payload),绝对不可能有上层没下层。

接着我们就分析每一层的报文结构

Ethernet头

以太帧有好多种,我们最常用到的是Ethernet II

首先是目的MAC 6个字节,然后源MAC6个字节,接下来数据类型2个字节。

整体报文结构:

  • 目标MAC地址(MAC destination address):目标主机的MAC物理地址;
  • 源MAC地址(MAC source address):本机MAC地址;
  • 802.1q(可选)
  • 类型(Type):常见的类型有IPv4,ARP等
  • 数据(Payload)
  • FCS(Frame check sequence)

Type类型:

  • IPv4: 0x0800
  • ARP:0x0806
  • PPPoE:0x8864
  • 802.1Q tag: 0x8100
  • IPV6: 0x86DD
  • MPLS Label:0x8847

然后是数据长度,46-1500字节。对于不定长的数据包,帧最后还有4个字节的FCS(Frame check sequence)

下面是一个以太帧头示例,该报文类型为IPv4(0x8000)

因此没有特殊情况Ethernet 头首部占14(6+6+2)个字节

IP层

IP协议在网络层,IP协议主要提供了IP编址,让主机可以在不同网段之间通信,报头信息解析如下:

1、4位版本号(Version):一般是IPv4或IPv6;

2、4位头部长度(Header Length):IP报头长度;这里所指示的长度,是以4个字节为一个单位。例如,一个IP包头的长度最长为“1111”,即15*4=60个字节。IP包头最小长度为20字节。

3、8位服务类型(Type of Service)

4、16位总长度(Total Length):IP报文的总长度,包括报头和数据;

5、16位标识符(Identification),标识每个已切分的数据包;

6、3位标记(Flags):第一位保留,第二位为DF(为1不将数据分包),第三位为MF(为1表示后面还有数据包);

7、13位包偏移(Fragmenet Offset):表示IP包在原数据包中的位置;

8、8位TTL,数据包的存活时长;

9、协议(Protocol):IP报头后面的报文协议,常用协议号:

  • 1 ICMP
  • 2 IGMP
  • 6 TCP
  • 17 UDP
  • 88 IGRP
  • 89 OSPF

10、头部检查和(Header Checksum)

11、源IP地址(Source Address):本机IP地址;

12、目的IP地址(Destination):目的IP地址。

一般IPv4的首部是20个字节。

TCP报文结构:

当上层应用向TCP发送一个大数据文件时,文件数据会被切割成一段段的报文段存放至TCP缓存中,那么报文段的大小应该是多少呢?这就要提到一个值最大报文段长度(Maximum Segment Size, MSS),而这个 MSS 值最终会受到链路层传输的数据长度即 最大传输单元(Maximum Transmission Unit, MTU)的限制。在以太网中链路层协议都具有1500字节的MTU,而报文段长度一般是1460字节,还有40字节是TCP/IP的首部。

  • 序号(Seq)确认号(Ack) 是用于可靠传输,简单来说是一个顺序关系。
  • 首部长度:TCP头的长度,即数据从何处开始。最大为15,(单位是32比特,即4个字节),与IP头中的长度定义相同。也就是对应的数值*4 才是实际字节。
  • 保留(Reserved):4bit,这些位必须是0。为了将来定义新的用途所保留
  • 标志(FLAG) 标记出包的一些作用
    • URG - 紧急: 当 URG 设置为 1 时,紧急数据指针指向的数据会被优先传递, 无需经过 TCP 缓存;
    • SYN - 同步: 表示开始会话请求;
    • RST - 复位: 用来关闭异常连接,即不需要通过 4 次分手;
    • PSH - 推送: 尽快将数据传递到上层,这种在交互式终端就会看到这个标记的包;
    • ACK - 应答: 用于确认确认号的值时有效的;
    • FIN - 结束: 结束连接,即发起 4 次分手;
    • ECE - 显示拥塞回显: 这是和拥塞控制有关系,是通过路由设备(支持 ECN 功能)为报文段添加的一个 flag;
    • CWR - 拥塞窗口减少: 发送端将通过降低发送窗口的大小来降低发送速率, 涉及拥塞控制
  • 接收窗口 用于作为流量控制,该字段用于指示接收方愿意接收的字节数量。
  • 校验和(Checksum):16bit。发送端基于数据内容计算一个数值,接收端要与发送端数值结果完全一样,才能证明数据的有效性。接收端checksum校验失败的时候会直接丢掉这个数据包。CheckSum是根据伪头+TCP头+TCP数据三部分进行计算的。
  • 紧急指针(Urgent Pointer):16位,在URG标志设置了时才有效。与序号字段的值相加后表示最后一个紧急数据的下一字节的序号,可以说这个字段是紧急指针相对当前序号的偏移。
  • 选项(Option):长度不定,但长度必须以是32bits的整数倍。常见的选项包括MSS、SACK、Timestamp等等

抓包分析

上面是针对一个网卡抓取的tcp 8000端口一次完成的http请求(119.1 是client端,119.31是server端)。

  • 44~46,3个包是3次握手
  • 47是一个http GET 请求,然后48是server 端对请求的ACK,注意这还不是response,只是tcp 协议规定的每次请求都要有对应的ACK 包回复。
  • 49是server端返回http response,50 是client 返回的ACK,同样也是tcp的规定。
  • 53~55,这3个包是4次挥手,但是为啥只捕获3个包,这个是因为做了优化,第2次和第3次都是server端发送给client端的包,直接将他们俩合并成了1个包。 这个网上有不少资料,这里不赘述,可以参考:美团二面:TCP 四次挥手,可以变成三次吗?-51CTO.COM

查看具体报文:

TCP 粘包&拆包

首先TCP 是面向流的,对他来说不存在包的概念,包是应用层的视角人为设定的一个模型。就像家里的水孔头,假设发送端是水孔头,接收端是地面,一个个水滴是数据包。稍微松开一点点开关,可以看到一滴滴水比较清晰的落下来,就像网络不繁忙,包之间间隔时间比较长的时候。再加大一点开关,就能看到原来的水珠变成了一条水线,这个时候接收端是分不清楚到底发了多少个数据包。从报文上看,就是一个tcp头,后面带的payload(就是data)可以是多次发送合并的内容。

因此粘包和拆包是同一个事情的两面,因为存在粘包,必然会导致别的包被拆断,因此存在3种可能的情况:

  1. 正常一个个数据包输出
  2. 多个数据包”粘“在了一起。
  3. 一个数据包被”拆“开了,这种包叫半包。

代码重现:

server端:

func main() {l, err := net.Listen("tcp", ":8888")if err != nil {panic(err)}fmt.Println("listen to 8888")for {conn, err := l.Accept()if err != nil {fmt.Println("conn err:", err)} else {go handleConn(conn)}}
}func handleConn(conn net.Conn) {defer conn.Close()defer fmt.Println("关闭")fmt.Println("新连接:", conn.RemoteAddr())result := bytes.NewBuffer(nil)var buf [1024]bytefor {n, err := conn.Read(buf[0:])result.Write(buf[0:n])if err != nil {if err == io.EOF {continue} else {fmt.Println("read err:", err)break}} else {fmt.Println("----------recv:", result.String())}result.Reset()}
}

client端:

func main() {data := []byte("{@@@@@@@@@@@一个完整的包@@@@@@@@@@@@@}")conn, err := net.DialTimeout("tcp", "localhost:8888", time.Second*30)if err != nil {fmt.Printf("connect failed, err : %v\n", err.Error())return}for i := 0; i < 1000; i++ {_, err = conn.Write(data)if err != nil {fmt.Printf("write failed , err : %v\n", err)break}}
}

先启动server端,再运行client端,从server端的输出就可以看到粘包现象

可以看到有些包粘到一起了,有些包被截断了。

什么时候需要考虑粘包&拆包

如果是直接处理tcp 层的数据,就必须考虑这个问题。如果是处理应用层的数据,就不需要了。因为应用层已经做了这块的处理。

解决思路:

  1. 定长分隔,每个数据包固定长度,不足用特殊字符填充。简单粗暴,缺点也多。
  2. 使用特殊字符分隔,如果数据中有该字符,需要进行转义,不然会出现bug。
  3. 在数据包中添加长度字段,相当于payload 中使用自定义的报文格式。这个兼容性较好,但是处理逻辑很复杂,需要考虑很多情况。

最后:

知道这些有什么用

应用层开发其实不用知道这么多底层细节,基本都是操作系统处理好tcp 层拿到有效payload 交给应用层去处理,只要关心应用层接收的数据就可以了。但是如果你需要分析网络包,或者需要直接获取网卡上的数据进行处理(比如流量录制),就需要对协议和报文有一定的认识。

这里尝试回答下最开始的2个问题,仅代表个人理解:

为什么要有协议:

协议是一种规范,保证大家都能按照这套规矩来协作,降低交互的成本。比如tcp 是一个可靠协议,规定了3次握手和4次回收,每次收到请求后要发送ack包,因为他是根据ack包来保证可靠传输的(顺序传输、丢包重传)。

协议为什么要对报文格式有约定?

首先分层带来报文的套娃结构,引入了复杂性。而且不同报文的长度不同,部分协议头长度也是可变的,如何让通信双方能够顺利编解码需要一套统一的规范。所以协议头的规范就很重要,这样大家能够根据协议头顺利的拿到下一层的报文。

TCP/IP 报文协议学习相关推荐

  1. 【转】:TCP/IP详解学习笔记(4)-ICMP协议,ping和Traceroute

    TCP/IP详解学习笔记(4)-ICMP协议,ping和Traceroute 分类:            TCP/IP详解学习笔记计算机网络2006-04-20 18:147970人阅读评论(1)收 ...

  2. 计算机网络学习之TCP/IP五层协议模型、TCP和UDP

    一.TCP/IP五层协议 TCP/IP 五层协议和 OSI 的七层协议对应关系如下: 应⽤层 (application layer):直接为应⽤进程提供服务.应⽤层协议定义的是应⽤进程间通讯和交互的规 ...

  3. TCP/IP详解学习笔记-基本概念

    为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样 ...

  4. 基础才是王道——TCP/IP详解学习笔记 这位仁兄写得太好了

    TCP/IP详解学习笔记 这位仁兄写得太好了 TCP/IP详解学习笔记   这位仁兄写得太好了. http://blog.csdn.net/goodboy1881/category/204448.as ...

  5. 【计算机网络】TCP / IP 四层协议

    TCP / IP 四层协议 一.概述 二.网际层 1. 概述 2. IP 地址 ① IPv4 ☯ NAT 技术 ② IPv6 三.运输层 1. TCP 协议 2. UDP 协议 四.应用层 1. 域名 ...

  6. TCP/IP 网络协议基础入门

    文章目录 1.TCP/IP简介 IP 地址 域名 MAC 地址 端口号 封装和分用 2.链路层介绍 控制帧的传输 差错控制 反馈重发 计时器 序号 流量控制 以太网 PPP(点对点协议) SLIP 与 ...

  7. 一文打败TCP/IP五层协议基础知识

    注意: 文章如有抄袭部分,请私信我. 未经允许,不得转载,如需转载,可以私信我. 前言 在很久之前,计算机刚刚被研究出来,当时的计算机还不能上网,玩游戏等,它只用于科学家,并没有给普通人使用. 后来, ...

  8. TCP/IP详解学习笔记(1)-基本概念

    为什么会有TCP/IP协议 在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样 ...

  9. TCP/IP详解学习笔记

    [TCP/IP详解学习笔记(1)基本概念] 为什么会有TCP/IP协议? 计算机型号多种多样,并且运行于不同操作系统.虽然电线把计算机连接到了一起,但是这些计算机无法"交流",所以 ...

最新文章

  1. Python内部机制。
  2. Scalaz(7)- typeclass:Applicative-idomatic function application
  3. 工业界如何解决NER问题?12个trick,与你分享~
  4. 科研实习 | 香港科技大学统计机器学习实验室张潼教授招收暑期科研实习生
  5. 解秘亿级网站的一本书——亿级流量网站架构核心技术
  6. jdk1.8之HashMap
  7. 用js实现鼠标点击爱心特效
  8. HTML网页设计制作大作业(div+css)
  9. 计算机一级操作题题库在线,全国计算机一级操作题「题库」
  10. 社交网络和社会计算入门路径
  11. 干货---ARCGIS拓扑规则说明详细讲解
  12. JS基础知识思维导图
  13. 跟着团子学SAP CS:SAP CS(客户服务)模块概览
  14. wireshark 过滤omci包_中兴OLT、ONU常见故障问题处理
  15. 开源TMS团队协作web系统
  16. Linux 配置网络案例
  17. 最新网狐旗舰版整理、编译和搭建教程
  18. 软件测试之测试主流技能
  19. VB获得迅雷资讯弹出网页的源代码
  20. 基于51单片机的超声波测距仪测液位及报警方案原理图设计

热门文章

  1. 简单的图标移入效果(css缩放)
  2. 项目一:使用python tkinter模块做简单的计算器
  3. Kubernetes(k8s)的Secret以密文的方式存储数据
  4. 提升汽车APP用户体验,火山引擎APMPlus的“独家秘笈”
  5. 福大软工 · 第十一次作业 - Alpha 事后诸葛亮(团队)
  6. php酷狗音乐API接口,酷狗音乐抓取api
  7. C++主流IDE推荐
  8. android逆向分析so,Android逆向——so反编译分析由浅入深(回帖奖励)
  9. 直播app代码公布:视频直播源码转盘功能的实现
  10. linux下qt实现计算器,QT实现简单计算器功能