用Wireshark抓的包发现TCP三次握手连续发了两边,如下图 为什么呢?
TCP有超时重传机制,如果在定时器范围内未收到确认,会重发数据包。

画图描述tcp三次握手建立连接的过程。
答:如下图所示。客户端主动发出请求,并令其SYN=1, 并设置S1Q序号值等于X;服务器端接收到请求之后进行响应,发送SYN=1,ACK=1,表示同意建立连接,开始分配服务器资源。同时服务器端发送序号seq=y,服务器期待收到的数据序号ack=x+1;客户端收到服务器的期待以后,并发送序号seq=x+1对应的数据,同时ack=y+1表示期待收到序号为y+1对应的数据;

TCP/UDP/HTTP/HTTPS
图片来源三次握手流程:为何不采用两次握手四次挥手流程:为何需要进入 TIME-WAIT 等待 2 MSL 时间才进入close状态为何握手需要三次而挥手需要四次三次握手和四次挥手简单举例三次握手四次挥手报文格式图片来源HTTP的请求报文包括:请求行(request line)、请求头部(header)、空行和请求数据(request data)四个部分组成。图片来源请求行包括: 请求方法,URL(包括参数信息),协议版本这些信息(GET /admin_ui/rdx/core/images/close.png HTTP/1.1)请求头部(Header)是一个个的key-value值,比如请求数据:GET方法没有携带数据, POST方法会携带一个bodyHTTP的响应报文包括:状态行,响应头,空行,数据(响应体)图片来源状态行包括:HTTP版本号,状态码和状态值组成。响应头类似请求头,是一系列key-value值空白行:同上,响应报文也用空白行来分隔header和数据响应体:响应的data,本例中是一段HTML数字中的第一位指定了响应类别,后两位无分类,响应类别有一下5种:状态码分类表2xx (3种)3xx (5种)4xx (4种)5xx (2种)1.1 长连接(Persistent Connection)HTTP1.1支持长连接和请求的流水线处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启长连接keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。HTTP1.0需要使用keep-alive参数来告知服务器端要建立一个长连接。1.2 节约带宽HTTP1.0中存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能。HTTP1.1支持只发送header信息(不带任何body信息),如果服务器认为客户端有权限请求服务器,则返回100,客户端接收到100才开始把请求body发送到服务器;如果返回401,客户端就可以不用发送请求body了节约了带宽。1.3 HOST域在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname),HTTP1.0没有host域。随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都支持host域,且请求消息中如果没有host域会报告一个错误(400 Bad Request)。1.4缓存处理在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。2.1 多路复用HTTP2.0使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级。HTTP1.1也可以多建立几个TCP连接,来支持处理更多并发的请求,但是创建TCP连接本身也是有开销的。图片来源2.2 头部压缩在HTTP1.1中,HTTP请求和响应都是由状态行、请求/响应头部、消息主体三部分组成。一般而言,消息主体都会经过gzip压缩,或者本身传输的就是压缩过后的二进制文件,但状态行和头部却没有经过任何压缩,直接以纯文本传输。随着Web功能越来越复杂,每个页面产生的请求数也越来越多,导致消耗在头部的流量越来越多,尤其是每次都要传输UserAgent、Cookie这类不会频繁变动的内容,完全是一种浪费。HTTP1.1不支持header数据的压缩,HTTP2.0使用HPACK算法对header的数据进行压缩,这样数据体积小了,在网络上传输就会更快。HTTP是明文传输,整个过程完全透明,任何人都能够在链路中截取、修改或者伪造请求,数据不具有可靠性。因此有了httpsHTTPS是身披SSL外壳的HTTP。HTTPS是一种通过计算机网络进行安全通信的传输协议,经由HTTP进行通信,利用SSL/TLS建立全信道,加密数据包。HTTPS使用的主要目的是提供对网站服务器的身份认证,同时保护交换数据的隐私与完整性。参考:https://juejin.im/entry/5981c5df518825359a2b9476

TCP状态转换图、TCP连接3次握手4次挥手
两将军问题:红蓝两军作战,蓝军战斗力强大,红1军或红2军与其单独作战都打不过蓝军,所以需要红一军与红二军联合对蓝军发起进攻,红军1首先通知红军2明早10点发起总攻,如图1-1,红军2接到消息需要回复“好的红军1,我已经收到你得消息,确认明早10点发动总攻”。因为消息传递路线必须经过蓝军营地,所以双方传递消息的信使很有可能被蓝军俘获。为了确保消息的可靠性,红1、红2双方在发出一个消息之后都想得到对方的消息回执。但是这会导致消息无线循环下去,如图1-2。那么如何解决这个可靠性的问题呢,其实没有办法解决,只要保证双方各自都有一次成功的发送、回执就可以了。两将军问题也存在网络世界里,客户端、服务器建立连接不可能无限的确认下去,只要保证客户端和服务器分别对自己的收、发能力做一次确认即可,如下图。 客户端和服务器分别对自己的收、发能力做一次确认至少需要3次握手。3次握手的具体过程、状态如下:(1)首先客户端和服务器都处于CLOSED状态。(2)服务器处于LISTEN状态,具体为服务器调用Socket、bind、listen函数,进入阻塞状态。(3)客户端发送SYN(同步序列编号),发送完毕客户端进入SYN_SENT状态。(4)服务端收到SYN,发送SYN+ASK,发送完毕进入SYN_RCVD状态。(5)客户端收到服务端发来的SYN+ASK,发送服务端等待的ASK,发送完毕客户端进入ESTABLISHED状态,准备数据传输,到此客户端已经满足了对自己收发能力的一次验证。(6)服务端收到客户端发来的ASK,与客户端一样,到此服务端也已经满足了对自己收发能力的一次验证,所以也进入ESTABLISHED状态,准备数据传输。(7)准备开始传输数据TCP断开连接有两种情况或者说是场景,1 客户端先断开连接,当然也可能是服务器先断开连接,总之是一前一后。 2 双方同时发起断开连接操作。下面分别介绍两种场景:(1)客户端先发起断开连接操作,客户端向服务端发送FIN,发送完毕客户端进入FIN_WAIT_1状态。(2)服务端收到客户端发来的FIN,服务端发送ACK,发送完毕进入CLOSED_WAIT状态。(3)客户端收到服务端的ACK回复,客户端进入FIN_WAIT_2状态,如果后面服务端没有回应客户端,在TCP协议层面来讲,客户端将永远停留在这个状态了,不过还好,操作系统着这块做了处理,有一个超时时间。(4)此时TCP连接进入半关闭状态,即客户端主,服务端从的这条线路已经关闭,不过服务端主,客户端从的这条线路还处于打开状态。(5)服务端向客户端发送FIN,发送完毕,服务端进入LAST_ASK状态。(6)客户端收到服务端的FIN后回复服务端ACK,回复完毕进入TIME_WAIT状态,为什么要进入这个状态?因为第6步是客户端的最后一条回复,服务端很有可能收不到,收不到服务端就会重发,所以客户端还要等待一会。(7)服务端收到客户端的ACK回复之后,不再做响应,回到初始的CLOSED状态,在连接池中等待下一次的复用。(8)客户端保持TIME_WAIT状态,超时之后同样进入CLOSED状态。场景二(1)客户端、服务器双方同时发送FIN,双方同时进入FIN_WAIT_1状态(2)双方都接到了对方的ACK,此时双方都会进入CLOSING状态。(3)双方同时进入TIME_WAIT状态,为什么要进入这个状态而不是直接进入CLOSED状态呢?假设客户端和服务端本次是第X次建立连接、关闭连接。如果立即关闭,随后建立第X+1次连接,建立连接成功之后,第X次的丢包的数据有可能绕了一大圈又回来了,那就会出现数据错误,为了避免这种情况所以要进入TIME_WAIT状态,以保证旧连接的数据不会再回来。(4)TIME_WAIT超时之后,双双进入CLOSED状态。有了上面对TCP连接3次握手4次挥手的介绍,再来理解TCP的状态图就不困难了,无非就是对TCP连接3次握手4次挥手过程的打包概述而已。

Go 语言自我提升 (三次握手 - 四次挥手 - TCP状态图 - udp - 网络文件传输)
三次握手: 1. 主动发起连接请求端(客户端),发送 SYN 标志位,携带数据包、包号2. 被动接收连接请求端(服务器),接收 SYN,回复 ACK,携带应答序列号。同时,发送SYN标志位,携带数据包、包号3. 主动发起连接请求端(客户端),接收SYN 标志位,回复 ACK。被动端(服务器)接收 ACK —— 标志着 三次握手建立完成( Accept()/Dial() 返回 )四次挥手:1. 主动请求断开连接端(客户端), 发送 FIN标志,携带数据包2. 被动接受断开连接端(服务器), 发送 ACK标志,携带应答序列号。 —— 半关闭完成。3. 被动接受断开连接端(服务器), 发送 FIN标志,携带数据包4. 主动请求断开连接端(客户端), 发送 最后一个 ACK标志,携带应答序列号。—— 发送完成,客户端不会直接退出,等 2MSL时长。等 2MSL待目的:确保服务器 收到最后一个ACK滑动窗口:通知对端本地存储数据的 缓冲区容量。—— write 函数在对端 缓冲区满时,有可能阻塞。TCP状态转换:1. 主动发起连接请求端:CLOSED ——> 发送SYN ——> SYN_SENT(了解) ——> 接收ACK、SYN,回发 ACK ——> ESTABLISHED (数据通信)2. 主动关闭连接请求端:ESTABLISHED ——> 发送FIN ——> FIN_WAIT_1 ——> 接收ACK ——> FIN_WAIT_2 (半关闭、主动端)——> 接收FIN、回复ACK ——> TIME_WAIT (主动端) ——> 等 2MSL 时长 ——> CLOSED3. 被动建立连接请求端:CLOSED ——> LISTEN ——> 接收SYN、发送ACK、SYN ——> SYN_RCVD ——> 接收 ACK ——> ESTABLISHED (数据通信)4. 被动断开连接请求端:ESTABLISHED ——> 接收 FIN、发送 ACK ——> CLOSE_WAIT ——> 发送 FIN ——> LAST_ACK ——> 接收ACK ——> CLOSEDwindows下查看TCP状态转换:netstat -an | findstr 端口号Linux下查看TCP状态转换:netstat -an | grep 端口号TCP和UDP对比:TCP: 面向连接的可靠的数据包传递。 针对不稳定的 网络层,完全弥补。ACKUDP:无连接不可靠的报文传输。 针对不稳定的 网络层,完全不弥补。还原网络真实状态。优点 缺点TCP:可靠、顺序、稳定 系统资源消耗大,程序实现繁复、速度慢UDP:系统资源消耗小,程序实现简单、速度快 不可靠、无序、不稳定使用场景:TCP:大文件、可靠数据传输。 对数据的 稳定性、准确性、一致性要求较高的场合。UDP:应用于对数据时效性要求较高的场合。 网络直播、电话会议、视频直播、网络游戏。UDP-CS-Server实现流程:1. 创建 udp地址结构 ResolveUDPAddr(“协议”, “IP:port”) ——> udpAddr 本质 struct{IP、port}2. 创建用于 数据通信的 socket ListenUDP(“协议”, udpAddr ) ——> udpConn (socket)3. 从客户端读取数据,获取对端的地址 udpConn.ReadFromUDP() ——> 返回:n,clientAddr, err4. 发送数据包给 客户端 udpConn.WriteToUDP("数据", clientAddr)UDP-CS-Client实现流程:1. 创建用于通信的 socket。 net.Dial("udp", "服务器IP:port") ——> udpConn (socket)2. 以后流程参见 TCP客户端实现源码。UDPserver默认就支持并发!------------------------------------命令行参数: 在main函数启动时,向整个程序传参。 【重点】语法: go run xxx.go argv1 argv2 argv3 argv4 。。。xxx.exe: 第 0 个参数。argv1 :第 1 个参数。argv2 :第 2 个参数。argv3 :第 3 个参数。argv4 :第 4 个参数。使用: list := os.Args 提取所有命令行参数。获取文件属性函数:os.stat(文件访问绝对路径) ——> fileInfo 接口fileInfo 包含 两个接口。Name() 获取文件名。 不带访问路径Size() 获取文件大小。网络文件传输 —— 发送端(客户端)1. 获取命令行参数,得到文件名(带路径)filePathlist := os.Args2. 使用 os.stat() 获取 文件名(不带路径)fileName3. 创建 用于数据传输的 socket net.Dial("tcp", “服务器IP+port”) —— conn4. 发送文件名(不带路径) 给接收端, conn.write()5. 读取 接收端回发“ok”,判断无误。封装函数 sendFile(filePath, conn) 发送文件内容6. 实现 sendFile(filePath, conn)1) 只读打开文件 os.Open(filePath)for {2) 从文件中读数据 f.Read(buf)3) 将读到的数据写到socket中 conn.write(buf[:n])4)判断读取文件的 结尾。 io.EOF. 跳出循环}网络文件传输 —— 接收端(服务器)1. 创建用于监听的 socket net.Listen() —— listener2. 借助listener 创建用于 通信的 socket listener.Accpet() —— conn3. 读取 conn.read() 发送端的 文件名, 保存至本地。4. 回发 “ok”应答 发送端。5. 封装函数,接收文件内容 recvFile(文件路径)1) f = os.Create(带有路径的文件名)for {2)从 socket中读取发送端发送的 文件内容 。 conn.read(buf)3) 将读到的数据 保存至本地文件 f.Write(buf[:n])4) 判断 读取conn 结束, 代表文件传输完成。 n == 0 break}

本文由 在线网速测试 整理编辑,转载请注明出处,原文链接:https://www.wangsu123.cn/news/61206.html。