最近在开始找实习, 自己的项目中用到了网络编程: TCP/IP 协议和 UDP 协议
结合已经面过的两个公司的面试官问的相关题目, 整理一下这俩协议的一些知识.
转自
TCP 通信的三次握手和四次撒手的详细流程(顿悟)
https://www.cnblogs.com/cy568searchx/p/3711670.html
这篇文章写得太好了, 为什么不在两次面试前看到这篇文章, 基本上讲清楚了所有问题
TCP(Transmission Control Protocol) 传输控制协议
三次握手
TCP 是主机对主机层的传输控制协议, 提供可靠的连接服务, 采用三次握手确认建立一个连接:
位码即 tcp 标志位, 有 6 种标示: SYN(synchronous 建立联机) ACK(acknowledgement 确认) PSH(push 传送) FIN(finish 结束) RST(reset 重置) URG(urgent 紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)
第一次握手: 主机 A 发送位码为 syn=1, 随机产生 seq number=1234567 的数据包到服务器, 主机 B 由 SYN=1 知道, A 要求建立联机;
第二次握手: 主机 B 收到请求后要确认联机信息, 向 A 发送 ack number=(主机 A 的 seq+1),syn=1,ack=1, 随机产生 seq=7654321 的包
第三次握手: 主机 A 收到后检查 ack number 是否正确, 即第一次发送的 seq number+1, 以及位码 ack 是否为 1, 若正确, 主机 A 会再发送 ack number=(主机 B 的 seq+1),ack=1, 主机 B 收到后确认 seq 值与 ack=1 则连接建立成功.
完成三次握手, 主机 A 与主机 B 开始传送数据.
在 TCP/IP 协议中, TCP 协议提供可靠的连接服务, 采用三次握手建立一个连接.
第一次握手: 建立连接时, 客户端发送 syn 包 (syn=j) 到服务器, 并进入 SYN_SEND 状态, 等待服务器确认;
第二次握手: 服务器收到 syn 包, 必须确认客户的 SYN(ack=j+1), 同时自己也发送一个 SYN 包(syn=k), 即 SYN+ACK 包, 此时服务器进入 SYN_RECV 状态; 第三次握手: 客户端收到服务器的 SYN+ACK 包, 向服务器发送确认包 ACK(ack=k+1), 此包发送完毕, 客户端和服务器进入 ESTABLISHED 状态, 完成三次握手. 完成三次握手, 客户端与服务器开始传送数据.
实例:
IP 192.168.1.116.3337 > 192.168.1.123.7788: S 3626544836:3626544836
IP 192.168.1.123.7788 > 192.168.1.116.3337: S 1739326486:1739326486 ack 3626544837
IP 192.168.1.116.3337 > 192.168.1.123.7788: ack 1739326487,ack 1
第一次握手: 192.168.1.116 发送位码 syn=1, 随机产生 seq number=3626544836 的数据包到 192.168.1.123,192.168.1.123 由 SYN=1 知道 192.168.1.116 要求建立联机;
第二次握手: 192.168.1.123 收到请求后要确认联机信息, 向 192.168.1.116 发送 ack number=3626544837,syn=1,ack=1, 随机产生 seq=1739326486 的包;
第三次握手: 192.168.1.116 收到后检查 ack number 是否正确, 即第一次发送的 seq number+1, 以及位码 ack 是否为 1, 若正确, 192.168.1.116 会再发送 ack number=1739326487,ack=1,192.168.1.123 收到后确认 seq=seq+1,ack=1 则连接建立成功.
图解:
一个三次握手的过程(图 1, 图 2)
(图 1)
(图 2)
第一次握手的标志位(图 3)
我们可以看到标志位里面只有个同步位, 也就是在做请求 (SYN)
(图 3)
第二次握手的标志位(图 4)
我们可以看到标志位里面有个确认位和同步位, 也就是在做应答 (SYN + ACK)
(图 4)
第三次握手的标志位(图 5)
我们可以看到标志位里面只有个确认位, 也就是再做再次确认 (ACK)
(图 5)
一个完整的三次握手也就是 请求 --- 应答 --- 再次确认
四次分手:
由于 TCP 连接是全双工的, 因此每个方向都必须单独进行关闭. 这个原则是当一方完成它的数据发送任务后就能发送一个 FIN 来终止这个方向的连接. 收到一个 FIN 只意味着这一方向上没有数据流动, 一个 TCP 连接在收到一个 FIN 后仍能发送数据. 首先进行关闭的一方将执行主动关闭, 而另一方执行被动关闭.
(
1
)客户端
A
发送一个
FIN
, 用来关闭客户
A
到服务器
B
的数据传送(报文段
4
).
(2)服务器 B 收到这个 FIN, 它发回一个 ACK, 确认序号为收到的序号加 1(报文段 5). 和 SYN 一样, 一个 FIN 将占用一个序号.
(
3
)服务器
B
关闭与客户端
A
的连接, 发送一个
FIN
给客户端
A
(报文段
6
).
(
4
)客户端
A
发回
ACK
报文确认, 并将确认序号设置为收到序号加
1
(报文段
7
).
状态详解:
CLOSED: 这个没什么好说的了, 表示初始状态.
LISTEN: 这个也是非常容易理解的一个状态, 表示服务器端的某个 SOCKET 处于监听状态, 可以接受连接了.
SYN_RCVD: 这个状态表示接受到了 SYN 报文, 在正常情况下, 这个状态是服务器端的 SOCKET 在建立 TCP 连接时的三次握手会话过程中的一个中间状态, 很短暂, 基本 上用 netstat 你是很难看到这种状态的, 除非你特意写了一个客户端测试程序, 故意将三次 TCP 握手过程中最后一个 ACK 报文不予发送. 因此这种状态 时, 当收到客户端的 ACK 报文后, 它会进入到 ESTABLISHED 状态.
SYN_SENT: 这个状态与 SYN_RCVD 遥想呼应, 当客户端 SOCKET 执行 CONNECT 连接时, 它首先发送 SYN 报文, 因此也随即它会进入到了 SYN_SENT 状态, 并等待服务端的发送三次握手中的第 2 个报文. SYN_SENT 状态表示客户端已发送 SYN 报文.
ESTABLISHED: 这个容易理解了, 表示连接已经建立了.
FIN_WAIT_1: 这个状态要好好解释一下, 其实 FIN_WAIT_1 和 FIN_WAIT_2 状态的真正含义都是表示等待对方的 FIN 报文. 而这两种状态的区别 是: FIN_WAIT_1 状态实际上是当 SOCKET 在 ESTABLISHED 状态时, 它想主动关闭连接, 向对方发送了 FIN 报文, 此时该 SOCKET 即 进入到 FIN_WAIT_1 状态. 而当对方回应 ACK 报文后, 则进入到 FIN_WAIT_2 状态, 当然在实际的正常情况下, 无论对方何种情况下, 都应该马 上回应 ACK 报文, 所以 FIN_WAIT_1 状态一般是比较难见到的, 而 FIN_WAIT_2 状态还有时常常可以用 netstat 看到.
FIN_WAIT_2: 上面已经详细解释了这种状态, 实际上 FIN_WAIT_2 状态下的 SOCKET, 表示半连接, 也即有一方要求 close 连接, 但另外还告诉对方, 我暂时还有点数据需要传送给你, 稍后再关闭连接.
TIME_WAIT: 表示收到了对方的 FIN 报文, 并发送出了 ACK 报文, 就等 2MSL 后即可回到 CLOSED 可用状态了. 如果 FIN_WAIT_1 状态下, 收到了对方同时带 FIN 标志和 ACK 标志的报文时, 可以直接进入到 TIME_WAIT 状态, 而无须经过 FIN_WAIT_2 状态.
CLOSING: 这种状态比较特殊, 实际情况中应该是很少见, 属于一种比较罕见的例外状态. 正常情况下, 当你发送 FIN 报文后, 按理来说是应该先收到 (或同时收到) 对方的 ACK 报文, 再收到对方的 FIN 报文. 但是 CLOSING 状态表示你发送 FIN 报文后, 并没有收到对方的 ACK 报文, 反而却也收到了对方的 FIN 报文. 什 么情况下会出现此种情况呢? 其实细想一下, 也不难得出结论: 那就是如果双方几乎在同时 close 一个 SOCKET 的话, 那么就出现了双方同时发送 FIN 报 文的情况, 也即会出现 CLOSING 状态, 表示双方都正在关闭 SOCKET 连接.
CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭. 怎么理解呢? 当对方 close 一个 SOCKET 后发送 FIN 报文给自己, 你系统毫无疑问地会回应一个 ACK 报文给对 方, 此时则进入到 CLOSE_WAIT 状态. 接下来呢, 实际上你真正需要考虑的事情是察看你是否还有数据发送给对方, 如果没有的话, 那么你也就可以 close 这个 SOCKET, 发送 FIN 报文给对方, 也即关闭连接. 所以你在 CLOSE_WAIT 状态下, 需要完成的事情是等待你去关闭连接.
LAST_ACK: 这个状态还是比较容易好理解的, 它是被动关闭一方在发送 FIN 报文后, 最后等待对方的 ACK 报文. 当收到 ACK 报文后, 也即可以进入到 CLOSED 可用状态了.
总结:
1.为什么建立连接协议是三次握手, 而关闭连接却是四次握手呢?
这是因为服务端的 LISTEN 状态下的 SOCKET 当收到 SYN 报文的建连请求后, 它可以把 ACK 和 SYN(ACK 起应答作用, 而 SYN 起同步作用)放在一个报文里来发送. 但关闭连接时, 当收到对方的 FIN 报文通知时, 它仅仅表示对方没有数据发送给你了; 但未必你所有的数据都全部发送给对方了, 所以你可以未必会马上会关闭 SOCKET, 也即你可能还需要发送一些数据给对方之后, 再发送 FIN 报文给对方来表示你同意现在可以关闭连接了, 所以它这里的 ACK 报文和 FIN 报文多数情况下都是分开发送的.
2.为什么 TIME_WAIT 状态还需要等 2MSL 后才能返回到 CLOSED 状态?
这是因为虽然双方都同意关闭连接了, 而且握手的 4 个报文也都协调和发送完毕, 按理可以直接回到 CLOSED 状态(就好比从 SYN_SEND 状态到 ESTABLISH 状态那样); 但是因为我们必须要假想网络是不可靠的, 你无法保证你最后发送的 ACK 报文会一定被对方收到, 因此对方处于 LAST_ACK 状态下的 SOCKET 可能会因为超时未收到 ACK 报文, 而重发 FIN 报文, 所以这个 TIME_WAIT 状态的作用就是用来重发可能丢失的 ACK 报文.
TCP 与 UDP 的区别:
(1)回答发送数据前是否存在建立连接的过程.
(2)TCP过确认机制, 丢包可以重发, 保证数据的正确性;UDP不保证正确性, 只是单纯的负责发送数据包(UDP 的开销更小数据传输速率更高, 因为不必进行收发数据的确认, 所以 UDP 的实时性更好).
(3) UDP 是面向报文的. 发送方的 UDP 对应用程序交下来的报文, 在添加首部后就向下交付给 IP 层. 既不拆分, 也不合并, 而是保留这些报文的边界, 因此, 应用程序需要选择合适的报文大小.
(4)UDP 的头部, 只有 8 个字节, 相对于 TCP 头部的 20 个字节信息包的额外开销很小.
使用 TCP/IP 和 UDP 的情况:
高可靠性的 TCP 服务提供面向连接的服务, 主要用于一次传输大量报文的情形, 如文件传输, 远程登录等;
高效率的 UDP 协议提供无连接的数据报服务, 用于一次传输少量的报文. 即使发生传输错误, 也可以重新传输并且不会为此付出多少代价.
当强调传输性能而不是传输的完整性时, 如: 音频和多媒体应用, UDP 是最好的选择.
TCP:
面向连接, 传输可靠 (保证数据正确性, 保证数据顺序), 用于传输大量数据 (流模式), 速度慢, 建立连接需要开销较多 (时间, 系统资源).
UDP: 面向非连接, 传输不可靠, 用于传输少量数据 (数据包模式), 速度快.
TCP 通过下列方式提供可靠性:
1. 应用数据被分割成 TCP 认为最合适的发送的数据块(报文段 / 段);
2.TCP 发出一个段后, 启动一个定时器, 等待目的端确认收到这个段, 若没有收到一个确认信息, 则重发;
3. 当 TCP 收到另一端 TCP 发来的数据, 则发送一个确认;
4.TCP 将保持其首部和检验和, 端到端的检验和, 目的是检测数据在传输过程中的任何变化, 有差错, 则丢弃段;
5.TCP 报文段作为 IP 数据报来传输则可能失序, 有必要进行重排序;
6.IP 数据包会发生重复, 则接收端需丢弃重复的数据;
7.TCP 控制流量.
来源: http://blog.csdn.net/u014029393/article/details/79218260