Wireshark:链路层抓包与分析工具
go语言:发起http网络通信请求
func main() {
r,_:=http.Get("http://www.baidu.com")
b,_ := ioutil.ReadAll(r.Body)
fmt.Println(string(b))
r.Body.Close()
}
1. 域名解析
序号50-53是获取DNS的阶段,可看到百度IP地址是183.232.231.172 。查看50包的详情可知道此过程采用了UDP协议。
(但为何后面57-58又获取了一次呢?)
2. TCP的三次握手
序号54-56是三次握手阶段
ACK:TCP协议规定只有ACK=1时有效,也规定连接建立后所有发送的报文的ACK必须为1。
SYN:在连接建立时用来步同序号。当SYN=1而ACK=0时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使SYN=1和ACK=1,因此SYN置1就表示这是一个连接请求或连接接受报文。
FIN:用来释放一个连接。当 FIN = 1 时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
(1)首先由Client发出请求连接即 SYN=1 ACK=0,TCP规定SYN=1时不能携带数据,但要消耗一个序号,因此声明自己的序号是 seq=x。
(2)然后 Server 进行回复确认,即 SYN=1 ACK=1 seq=y,ack=x+1。
(3)再然后 Client 再进行一次确认,但不用SYN 了,这时即为 ACK=1,seq=x+1, ack=y+1。再次确认可保证不会因延迟等原因重复发送的请求连接而直接建立新连接。
3. 建立TCP连接后发起HTTP请求
序号59发起GET请求
TCP三次握手建立连接成功后,客户端按照指定的格式开始向服务端发送HTTP请求,服务端接收请求后,解析HTTP请求,处理完业务逻辑,最后返回一个具有标准格式的HTTP响应给客户端。
如上图所示HTTP请求格式如下所示四部分组成,分别是请求行、请求头、空行、消息体,每部分内容占一行。
请求行:由三部分组成:分别是请求方法(GET/POST/DELETE/PUT/HEAD)、URI路径、HTTP版本号。
请求头:缓存相关信息(Cache-Control,If-Modified-Since)、客户端身份信息(User-Agent)等键值对信息。
空行。
主体:客户端发给服务端的请求数据,这部分数据并不是每个请求必须的。
服务器响应HTTP请求
传输过程中不免发生传输错误,例如丢包、乱序,TCP协议在此过程中及时纠错保证数据正确到达。图中标黑的包就是发生的错误,错误是接收方通过判断收到包的序列号seq和包长度等参数是否有误发现的。
下图中可见本包序列号seq:18900,len:1440,和下个包正确的序列号seq:20340
4.四次挥手
上图中最后一条标红的包是接收方向服务器发送了异常关闭连接的RST标志,此时立即断开连接,不必等四次握手断开,前面并没有发送FIN标志的包。异常原因应该是由于KEEPALIVE机制的存在,客户端的go程序停止通信但连接仍暂时保持,程序突然结束产生异常。如果不采用默认client,而是新建client并设置DisableKeepAlives为true,则会发送FIN标志正常断开。
golang强制短连接:除了在请求的头里加上connection:close
, 也可以设置request
结构体Close
成员变量为true
,比如:
req, _ := http.NewRequest("Get", "http://example.com", nil)
req.Close = true
正常断开连接应该是下面四个步骤:
第一次挥手:主机1(可以使客户端,也可以是服务器端),设置Sequence Number
和Acknowledgment Number
,向主机2发送一个FIN
报文段;此时,主机1进入FIN_WAIT_1
状态;这表示主机1没有数据要发送给主机2了;
第二次挥手:主机2收到了主机1发送的FIN
报文段,向主机1回一个ACK
报文段,Acknowledgment Number
为Sequence Number
加1;主机1进入FIN_WAIT_2
状态;主机2告诉主机1,我也没有数据要发送了,可以进行关闭连接了;
第三次挥手:主机2向主机1发送FIN
报文段,请求关闭连接,同时主机2进入CLOSE_WAIT
状态;
第四次挥手:主机1收到主机2发送的FIN
报文段,向主机2发送ACK
报文段,然后主机1进入TIME_WAIT
状态;主机2收到主机1的ACK
报文段以后,就关闭连接;此时,主机1等待2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,主机1也可以关闭连接了。