- 浏览: 909904 次
文章分类
最新评论
-
eap777:
很庆幸楼主把读书这个习惯坚持下来了
两年的java高手路 -
bestxiaok:
面对潮起潮落,即使公司大幅度萎缩,我们不仅要淡定,也要矢志不移 ...
任正非首谈接班人制度 -
ayaga:
关键是要看真的,不要看假的。
创业者需要了解的基本财务知识 -
ayaga:
跟着党学,华为就是个小社会。
任正非首谈接班人制度 -
zzchsppy:
本人初学者 谢谢
指针
TCP通信流程解析
B/S
通信简述
整个计算机网络的实现体现为协议的实现,
TCP/IP
协议是
Internet
的核心协议,
HTTP
协议是比
TCP
更高层次的应用层协议。
HTTP
(
HyperText Transfer Protocol
,超文本传输协议)是互联网上应用最为广泛的一种网络协议。所有的
WWW
文件都必须遵守这个标准。设计
HTTP
的初衷是为了提供一种发布和接收
HTML
页面的方法。
浏览器(
Web Browser
)负责与服务器建立连接,下载网页(包括资源文件及
JS
脚本文件)到本地,并最终渲染出页面。
JS
脚本文件运行在客户端,负责客户端一些行为响应或预处理,例如提交表单前的数据校验、鼠标事件处理等交互。由此可见,浏览器(
Browser
)一方面充当了
C/S
通信架构中
C
角色
,另一方面它是
HTML/JavaScript
的解析渲染引擎(
Analyze Render Engine
)。
在浏览器地址栏敲入
“http://www.baidu.com/
”
,按下回车键,浏览器中呈现出百度首页。这样一种情景我们再熟悉不过,本文通过
wireshark
抓取这一过程的
TCP/IP
数据包,结合
TCP
协议分析
HTTP
通信的基本流程。
MTU
和
MSS
本文用到的抓包工具为
wireshark
,它的前身是赫赫有名的
Ethereal
。
wireshark
以太网帧的封包格式为:
Frame
= Ethernet Header + IP Header + TCP Header + TCP Segment Data
(
1
)
Ethernet Header = 14
Byte = Dst Physical Address
(
6 Byte
)
+
Src Physical Address
(
6 Byte
)
+ Type
(
2 Byte
),以太网帧头以下称之为数据帧
。
(
2
)
IP Header = 20
Byte
(
without options field
),数据在
IP
层称为
Datagram
,分片称为
Fragment
。
(
3
)
TCP Header = 20
Byte
(
without options field
),数据在
TCP
层称为
Stream
,分段称为
Segment
(
UDP
中称为
Message
)
。
(
4
)
54
个字节后为
TCP
数据负载部分(
Data Portion
),即应用层用户数据。
Ethernet Header
以下的
IP
数据报最大传输单位为
MTU
(
Maximum Transmission Unit
,
Effect of short board
),对于大多数使用以太网的局域网来说,
MTU=1500
。
TCP
数据包每次能够传输的最大数据分段为
MSS
,为了达到最佳的传输效能,在建立
TCP
连接时双方协商
MSS
值,双方提供的
MSS
值的最小值为这次连接的最大
MSS
值。
MSS
往往基于
MTU
计算出来,通常
MSS=MTU-sizeof(IP Header)-sizeof(TCP Header)=1500-20-20=1460
。
这样,数据经过本地
TCP
层分段后,交给本地
IP
层,在本地
IP
层就不需要分片了。但是在下一跳路由(
Next Hop
)的邻居路由器上可能发生
IP
分片!因为路由器的网卡的
MTU
可能小于需要转发的
IP
数据报的大小。这时候,在路由器上可能发生两种情况:
(
1
)
.
如果源发送端设置了这个
IP
数据包可以分片(
May Fragment
,
DF=0
),路由器将
IP
数据报分片后转发。
(
2
)
.
如果源发送端设置了这个
IP
数据报不可以分片(
Don’t Fragment
,
DF=1
),路由器将
IP
数据报丢弃,并发送
ICMP
分片错误消息给源发送端。
关于
MTU
的探测,参考《
Path MTU discovery
》。我们
可以通过基于
ICMP
协议的
ping
命令来探测从本机出发到目标机器上路由上的
MTU
,详见下文。
TCP
和
UDP
在基于传输层(
TCP/UDP
)的应用开发中,为了最后的程序优化,应避免端到端的任何一个节点上出现
IP
分片。
TCP
的
MSS
协商机制加上序列号确认机制,基本上能够保证数据的可靠传输。
UDP
协议在
IP
协议的基础上,只增加了传输层的端口(
Source Port+Destination Port
)、
UDP
数据包长(
Length = Header+Data
)以及检验和(
Checksum
)。因此,基于
UDP
开发应用程序时,数据包需要结合
IP
分片情况考虑。对于以太局域网,往往取
UDP
数据包长
Length<=MTU-sizeof(IP Header)=1480
,故
UDP
数据负载量小于或等于
1472
(
Length-UDP Header
);对于公网,
ipv4
最小
MTU
为
576
,
UDP
数据负载量小于或等于
536
。
“
向外”
NAT
在内网和公网之间提供了一个“
不对称”
桥的映射。“
向外”
NAT
在默认情况下只允许向外的
session
穿越
NAT
:从外向内的的数据包都会被丢弃掉,除非
NAT
设备事先已经定义了这些从外向内的数据包是已存在的内网
session
的一部分。对于一方在
LAN
,一方在
WAN
的
UDP
通信,鉴于
UDP
通信不事先建立虚拟链路,
NAT
后面的
LAN
通信方需先发送消息给
WAN
通信方以洞穿
NAT
,然后才可以进行双向通信,这即是常提到的
“UDP
打洞(
Hole Punching
)
”
问题。
TCP
连接百度过程解析
下文对百度的完整抓包建立在不使用
缓存的基础上。如若主机存有百度站点的
cookie
和脱机缓存(
Offline Cache
),则不会再请求地址栏图标
favicon.ico
;请求
/js/bdsug.js?v=1.0.3.0
可能回应
“HTTP/1.1 304 Not Modified”
。可在浏览器打开百度首页后,Ctrl+F5强制刷新,不使用缓存,也可参考《
浏览器清除缓存方法
》。
以下为访问百度过程,
wireshark
抓包数据。对于直接通过
Ethernet
联网的机器,
Wireshark Capture Filter
为
host www.baidu.com
;对于通过
PPP over Ethernet
(
PPPoE
)联网的机器,
Wireshark Capture Filter
为
pppoes
and host www.baidu.com
。以下抓包示例
直接通过
Ethernet
联网访问百度的过程。可点击图片超链接下载pcap文件,使用wireshark软件查看。
为方便起见,以下将客户端(浏览器)简称为
C
,将服务器(百度)简称为
S
。
1 . TCP 三次握手建立连接
“http://” 标识 WWW 访问协议为 HTTP ,根据规则,只有底层协议建立连接之后才能进行更高层协议的连接。在浏览器地址栏输入地址后按下回车键的瞬间, C 建立与 S (机器名为 www.baidu.com , DNS 解析出来的 IP 为 220.181.6.175 )的 TCP 80 连接( HTTP 默认使用 TCP 80 端口)。
以下为三次握手建立 TCP 连接的数据包( Packet1-Packet3 )。
1 192.168.89.125:5672 → 220.181.6.175:80 TCP( 协议 ) 62( 以太网 帧长 )
amqp > http [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM =1
2 220.181.6.175:80 → 192.168.89.125:5672 TCP 62
http > amqp [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 SACK_PERM=1
3 192.168.89.125:5672 → 220.181.6.175:80 TCP 54
amqp > http [ACK] Seq=1 Ack=1 Win=65535 Len=0
三次握手建立 TCP 连接的流程如下:
C(Browser) S(www.baidu.com)
1. CLOSED LISTEN
2. SYN-SENT → <SEQ=0><CTL=SYN> → SYN-RECEIVED
3. ESTABLISHED ← <SEQ=0><ACK=1><CTL=SYN,ACK> ← SYN-RECEIVED
4. ESTABLISHED → <SEQ=1><ACK=1><CTL=ACK> → ESTABLISHED
3-Way Handshake for Connection Synchronization
三次握手的 socket 层执行逻辑
S 调用 socket 的 listen 函数进入监听状态; C 调用 connect 函数连接 S : [SYN] , S 调用 accept 函数接受 C 的连接并发起与 C 方向上的连接: [SYN,ACK] 。 C 发送 [ACK] 完成三次握手, connect 函数返回; S 收到 C 发送的 [ACK] 后, accept 函数返回。
关于 Seq 和 Ack
Seq 即 Sequence Number , 为源端 ( source ) 的发送序列号 ; Ack 即 Acknowledgment Number , 为目的端 ( destination ) 的接收确认序列号 。在 Wireshark Display Filter 中,可使用 tcp.seq 或 tcp.ack 过滤。
在 Packet1 中, C:5672 向 S:80 发送 SYN 握手包, Seq=0(relative sequence number) ;在 Packet2 中 , S:80 向 C:5672 发送 ACK 握手回应包, Ack=1(relative sequence number) ,同时发送 SYN 握手包, Seq=0(relative sequence number) ;在 Packet3 中, C:5672 向 S:80 发送 ACK 握手回应包, Seq=1 , Ack=1 。
至此, Seq=1 为 C 的 Initial Sequence Number ( ISN ),后期某一时刻的 Seq=ISN+ 累计发送量 (cumulative sent) ; Ack=1 为 C 的 Initial Acknowledge Number ( IAN ),后期某一时刻的 Ack=IAN+ 累计接收量 (cumulative received) 。对于 S 而言, Seq 和 Ack 情同此理。
参考
:《TCP Analyze Sequence Numbers
》、《Understanding TCP Sequence and Acknowledgement Numbers
》
2 . TCP 获取网站数据流程
连接建立后,下一步发送( “GET / HTTP/1.1” )请求( Request ) HTML 页面,这里 “/” 表示 S 的默认首页, “GET” 为 HTTP Request Method ; “/” 为 Request-URI ,这里为相对地址; HTTP/1.1 表示使用的 HTTP 协议版本号为 1.1 。
以下为 HTTP GET 请求数据包( Packet4 )。
4 192.168.89.125:5672 → 220.181.6.175:80 HTTP 417
GET / HTTP/1.1
HTTP GET 报文长 =417-54=363 个字节,其中 Next sequence number: 364(relative sequence number) 表示,若 在规定的时间内收到S 响应 Ack=364 ,表明该报文发送成功,可以发送下一个报文( Seq=364 );否则重传(TCP Retransmitssion )。序列号确认机制是 TCP 可靠性传输的保障。
S ( http )收到 HTTP GET 报文(共 363 个字节),向 C ( amqp )发送 TCP 确认报文 ( Packet5 )。
5 220.181.6.175:80 → 192.168.89.125:5672 TCP 60
http > amqp [ACK] Seq=1 Ack=364 Win=6432 Len=0
这里 Seq=1, 为 S 的 ISN ,意为已发送过 SYN 。 Packet2 中, Ack=1 为 S 的 IAN 。这里的 Ack-IAN=364-1=363 表示 S 已经从 C 接收到 363 个字节,即 HTTP GET 报文。同时,Ack=364也是S期待C发送的下一个TCP报文序列号(上面分析的 Next sequence number) 。
接下来, S 向 C 发送 Http Response ,根据 HTTP 协议,先发响应头( Response Header ),再发百度首页 HTML 文件。
Http Response Header 报文 ( Packet6 ) 如下 。
6 220.181.6.175:80 → 192.168.89.125:5672 TCP 465
[ TCP segment of a reassembled PDU ]
其部分内容如下:
======================================
HTTP/1.1 200 OK
……
Content-Length: 2139
Content-Type: text/html;charset=gb2312
Content-Encoding: gzip
======================================
S 响应 C 的 “GET / HTTP/1.1” 请求,先发送带 [PSH ] 标识的 411 个字节的 Http Response Header ( Packet 6 )。
TCP 头部 [PSH] 标识置位,敦促 C 将缓存的数据推送给应用程序,即先处理 Http Response Header ,实际上是一种 “ 截流 ” 通知。相应 C 的 socket 调用 send 时 在 IPPROTO_TCP 选项级别设置 TCP_NODELAY 为 TRUE 禁用 Nagle 算法可以 “ 保留发送边界 ” ,以防粘连。
尽管握手协商的 MSS 为 1460 ,但服务器或者代理平衡服务器,每次发送过来的 TCP 数据最多只有 1420 个字节 。 可以使用 ping -f -l size target_name 命令向指定目标 target_name 发送指定字节量的 ICMP 报文,其中 -l size 指定发送缓冲区的大小; -f 则表示在 IP 数据报中设置不分片( Don’t Fragment ),这样便可探测出到目标路径上的 MTU 。
执行“ ping -f -l 1452 www.baidu.com ”的结果如下:
220.181.6.18 的 Ping 统计信息 :
数据包 : 已发送 = 4 ,已接收 = 4 ,丢失 = 0 (0% 丢失 )
执行“ ping -f -l 1453 www.baidu.com ”的结果如下:
需要拆分数据包但是设置 DF 。
220.181.6.18 的 Ping 统计信息 :
数据包 : 已发送 = 4 ,已接收 = 0 ,丢失 = 4 (100% 丢失 )
从以上 ping 结果可知,在不分片时,从本机出发到百度的路由上能通过的最大数据量为 1452 ,由此推算出 MTU{local,baidu}=sizeof(IP Header)+ sizeof(ICMP Header)+sizeof(ICMP Data Portion)=20+8+1452=1480 。
S 调用 socket 的 send 函数发送 2139 个字节的 Http Response Content ( Packet 7 、 Packet 9 ),在 TCP 层将分解为两段( segment )后再发出去。
7 220.181.6.175:80 → 192.168.89.125:5672 TCP 1474
[TCP segment of a reassembled PDU]
由 “Content-Length: 2139” 可知, HTML 文件还有 2139-(1474-54)=719 个字节。但此时, C 已经发送了确认报文 ( Packet8 ) 。
8 192.168.89.125:5672 → 220.181.6.175:80 TCP 54
amqp > http [ACK] Seq=364 Ack=1832 Win=65535 Len=0
Seq-ISN=364-1=363 ,表示 C 已经发出了 363 个字节,上边已经收到了 S 的确认。 Ack-IAN=1832-1=(465-54)+(1474-54) ,表示 C 至此已经接收到 S 发来的 1831 个字节。
接下来, C 收到 HTML 文件剩余的 719 个字节,报文 ( Packet9 )如下。
9 220.181.6.175:80 → 192.168.89.125:5672 HTTP 773
HTTP/1.1 200 OK
至此, C 收到 S 发送过来的全部 HTTP 响应报文,即百度首页 HTML 内容 (text/html) 。
Packet6 、 Packet7 和 Packet9 的 ACK 都是 364 ,这是因为这三个segment都是针对 Packet4 的 TCP 响应。S将百度首页HTML文件(一个完整的HTTP报文)按照MSS分段提交给TCP层。 在 Wireshark 中可以看到 Packet9 的报文中有以下 reassemble 信息:
[Reassembled TCP segments (2555 bytes): #6(411),#7(1420),#9(719)]
[Frame: 6, payload: 0-410(411 bytes)]
[Frame: 7, payload: 411-1830(1420 bytes)]
[Frame: 9, payload: 1831-2549(719 bytes)]
C ( amqp )接收到百度首页的 HTML 文件后,开始解析渲染。在解析过程中,发现页面中含有百度的 logo 资源 baidu_logo.gif ,并且需要 bdsug.js 脚本。
<img src=" http://www.baidu.com/img/baidu_logo.gif " width="270" height="129" usemap="#mp">
{d.write('<script src=http://www.baidu.com/js/bdsug.js?v=1.0.3.0><//script>')}
于是上面那个连接( C:5672 )继续向 S 请求 logo 图标资源,报文( Packet10 )如下。
10 192.168.89.125:5672 → 220.181.6.175:80 HTTP 492
GET /img/baidu_logo.gif HTTP/1.1
与此同时, C ( jms )新建一个连接( TCP 5 673 )向 S 请求 js 脚本文件。 报文( Packet11 )如下。
11 192.168.89.125:5673 → 220.181.6.175:80 TCP 62
jms > http [SYN] Seq=0 Win=65535 Len=0 MSS=1460 SACK_PERM=1
( Packet12 ) Packet13 、 Packet14 、 Packet16 和 Packet17 为对 Packet10 的 TCP 响应(它们的 Ack=802 ), 在逻辑上它们是一个完整的 TCP 报文。其 Http Response Content 为图片文件 baidu_logo.gif 。我们在 Wireshark 中可以看到 Packet17 的报文中有以下 reassemble 信息:
[Reassembled TCP segments (1801 bytes): #13(312),#14(1420),#16(28) ,#17(41)]
[Frame: 13, payload: 0-311(312 bytes)]
[Frame: 14, payload: 312-1731(1420 bytes)]
[Frame: 16, payload: 1732-1759(28 bytes)]
[Frame: 17, payload: 1760-1800(41 bytes)]
Packet11-Packet19-Packet20 完成新连接的三次握手。然后, C ( jms )发送 “ GET /js/bdsug.js?v=1.0.3.0 HTTP/1.1 ” 报文( Packet21 ),以获取 bdsug.js 脚本文件。
21 192.168.89.125:5673 → 220.181.6.175:80 HTTP 465
GET /js/bdsug.js?v=1.0.3.0 HTTP/1.1
( Packet22 ) Packet23 、 Packet24 、 Packet26 和 Packet27 为对 Packet21 的 TCP 响应(它们的 Ack=412 ), 在逻辑上它们是一个完整的 TCP 报文。其 Http Response Content 为脚本文件 bdsug.js 。我们在 Wireshark 中可以看到 Packet27 的报文中有以下 reassemble 信息:
[Reassembled TCP segments (3897 bytes): #23(310),#24(1420),#26(1420) ,#27(747)]
[Frame: 23, payload: 0-309(310 bytes)]
[Frame: 24, payload: 310-1729(1420 bytes)]
[Frame: 26, payload: 1730-3149(1420 bytes)]
[Frame: 27, payload: 3150-3896(747 bytes)]
通常,浏览器会自动的搜索网站的根目录,只要它发现了 favicon.ico 这个文件,就把它下载下来作为网站地址栏图标。于是, C ( amqp )还将发起 “ GET /favicon.ico HTTP/1.1 ” 请求 网站地址栏图标,见报文 Packet29 。
3 . TCP 四次挥手关闭连接
经 Packet28 确认收到了完整的 japplication/javascript 文件后,链路 1 (本地端口 5673 )使命结束, S 关闭该链路,进入四次挥手关闭双向连接。
( Packet30 ) Packet31 和 Packet32 为对 Packet29 的 TCP 响应(它们的 Ack=1201 )。 经 Packet33 确认收到了完整的 image/x-icon 文件后,链路 2 (本地端口 5672 )使命结束, S 关闭该链路,进入四次挥手关闭双向连接。
为什么握手是三次,而挥手是四次呢?这是因为握手时,服务器往往在答应建立连接时,也建立与客户端的连接,即所谓的双向连接。所以,在 Packet2 中,服务器将 ACK 和 SYN 打包发出。挥手,即关闭连接,往往只是表明挥手方不再发送数据(无数据可发),而接收通道依然有效(依然可以接受数据)。当对方也挥手时,则表明对方也无数据可发了,此时双向连接真正关闭。
参考:
《 浏览器 /网页工作原理 》《 What really happens when you navigate to a URL 》
《 HTTP通信过程分析 》
《 究竟什么是 HTTP连接 》
《 一次完整的 HTTP通信步骤 》
相关推荐
《TCP通信流程解析》博文中TCP连接百度抓包文件,使用wireshark打开。
最近实习,在学习cisco路由器和交换机相关知识。这篇PPT是我自己根据所学知识,并上网查阅...主要讲述了同一VLAN间数据包传输过程和不同VLAN间数据包如何传输。涉及到三层交换机相关知识。因为初学,有不足地方请指正。
TCP/IP协议详解 完整目录 第一部分 TCP/IP基础 第1章 开放式通信模型简介 1 1.1 开放式网络的发展 1 1.1.1 通信处理层次化 2 1.1.2 OSI参考模型 3 1.1.3 模型的使用 5 1.2 TCP/IP参考模型 7 1.3 小结 7 第2章 TCP/IP...
Modbus Poll和Modbus Slave是两款非常流行的Modbus设备仿真软件,支持Modbus RTU/ASCII和Modbus TCP/IP协议 ,经常用于测试和调试Modbus设备,观察Modbus通信过程中的各种报文。 当用于支持Modbus RTU/ASCII协议时,...
完整的TCP传输过程,该部分代码解决了IP解析,端口被占用问题。可以供初学者进行Java的学习,以便更好的了解Java端到端之间的连接,数据流的流动。
6.7 名字服务解析过程 52 6.7.1 递归查询 52 6.7.2 叠代查询 52 6.8 高速缓存 52 6.9 反向解析(Pointer)查询 52 6.10 DNS安全 52 6.11 资源记录 53 6.12 小结 54 第7章 WINS 55 7.1 NetBIOS 55 7.2 NetBIOS名字解析 ...
Qt设备状态检测系统源代码串口 网络TCP UDPModbus通信协议带有报警声音详细注释 设备状态检测源代码支持串口网口TcpUdp播放音频LED显示采用modbus协议 提供,提供详细注释,提供设计文档 使用说明介绍 1.功能介绍: ...
本书内容十分丰富,几乎涵盖了有关TCP/IP的各个方面,包括开放式通信模型、TCP/IP通信模型、IP网络中的命名和寻址机制、地址解析及反向地址解析协议、DNS域字服务器、WINS、地址发现协议、IPv6、IP网络中的路由协议...
6.7 名字服务解析过程 52 6.7.1 递归查询 52 6.7.2 叠代查询 52 6.8 高速缓存 52 6.9 反向解析(Pointer)查询 52 6.10 DNS安全 52 6.11 资源记录 53 6.12 小结 54 第7章 WINS 55 7.1 NetBIOS 55 7.2 NetBIOS名字解析 ...
TCP三次握手四次挥手的过程详解.mdTCP三次握手四次挥手的过程详解.mdTCP三次握手四次挥手的过程详解.md
6.7 名字服务解析过程 52 6.7.1 递归查询 52 6.7.2 叠代查询 52 6.8 高速缓存 52 6.9 反向解析(Pointer)查询 52 6.10 DNS安全 52 6.11 资源记录 53 6.12 小结 54 第7章 WINS 55 7.1 NetBIOS 55 7.2 NetBIOS名字解析 ...
本文档使用Packet Sender工具模拟tcp客户端接入平台。 创建协议 请参考创建协议 创建设备型号 ... ... i.... ii....注意:本文档使用javascript自定义脚本的方式解析消息。 自定义脚本: var BytesUtils =
简单的Modbus通信,主要是用于简单了解Modbus通信的编程流程,以及Modbus通信signed和long DC BA通信数据的转化原理,内含源码,操作手册,应用程序。如有错误或者不到指出,包括注释理解的缺陷,请联系我指正,谢谢...