简单理解网络通信和TCP/IP协议

 

各层解释

应用层

为应用程序提供服务

表示层

数据格式转换、数据加密

会话层

建立管理和维护会话

传输层

建立管理和维护端到端的链接

网络层

IP选址和路由选择

数据链路层

提供介质访问和链路管理

物理层

计算机硬件

1.2 TCP/IP模型

OSI 模型比较复杂且学术化,所以我们实际使用的 TCP/IP 模型,分 5 层,物理层、数据链路层也有 TCP/IP 模型将物理层、数据链路层合称为网络接口层,与之对应的,协议就被称为 TCP/IP 四层协议模型)、网络层、传输层、应用层。

1.3 TCP/IP协议族

由网络层的IP协议和传输层的TCP协议组成。协议采用五层结构,由很多协议组成,并且分布在不同的层,是互联网的基础通信架构。

(1)IP协议往往用来确认网络中唯一一台计算设备。网络层就是为数据封装IP地址,包括源IP和目标IP。

(2)TCP和UDP是传输层协议,主要是为两台主机上的应用程序提供端到端的通信。

(3)TCP协议类似于打电话,接通之后确认身份,听不清的要求重说,说的快的要求慢点。TCP是面向链接的可靠协议。通过“重传肯定确认”机制实现传输的可靠性。采用“滑动窗口”的方式进行传输的流量控制。

(4)UDP协议是无连接的不可靠的传输协议,类似于日常生活中通过物流寄送东西,不需要知道对方是不是能够接收到,也不需要确认,可能会出现丢包现象。

(5)当然我们可以绕开TCP和UDP,而直接使用IP,比如Linux内核中的LVC就是基于IP层进行负载均衡平衡调度的。甚至可以直接访问数据链路层,比如tcpdump。

二、TCP/IP网络传输中的数据

每个网络分层都会对所发送的数据附加一个首部,这个首部包含了该层的必要信息。从下一层看,上一层的包是本层的数据部分。

网络传输中的数据包括两个部分,一部分是协议相关的首部,另一部分是上层传过来的数据。首部的结构由本层网络协议规范定义,首部明确标明了协议应该如何读取数据。如下图所示:

三、地址和端口号

3.1 MAC地址

MAC地址又称局域网地址、以太网地址或者物理地址,是由厂商写在网卡的BIOS里,与网络无关。地址共48位,前24位由IEEE决定如何分配,后24为由设备生产商自行制定。

3.2 IP地址

互联网上每个网络和每个主机设备唯一的逻辑地址,用来和物理地址区分。

IP地址分为:IPV4和IPV6,IPV4是由32位二进制组成,通常定义成4个8位二进制,可以理解成4个字节,格式为:A.B.C.D,其中ABCD为0-255的十进制数字。例如:192.168.75.111

IP协议属于网络层,MAC地址属于数据链路层,数据链路层协议使数据从一个节点传递到相同链路的另外一个节点。而网络层协议使数据从一个网络传递到另外一个网络。

3.3 端口号

端口号用于识别同一台计算机上进行通信的不同的应用程序,因此也被称为程序地址。

一台计算机上可以同时运行很多不同的应用程序,传输层协议正是利用这些端口号识别本机中正在进行通信的应用程序,并准确的将数据传输。

3.4 为什么端口号有65535个?

因为在TCP、UDP的协议报文的开头,会分别有16位二进制来存储源端口号和目标端口号,所以端口号的个数为2^16=65536个,但是0号端口用来表示所有端口,所以实际可用的端口是65535个。

3.5 端口号的确定

(1)标准既定端口号:它是指应用程序都自己制定了端口号,但并不是随意的端口号。例如HTTP、FTP、TELNET等广泛使用的应用协议中所使用的端口号就是固定的。这些端口号被称为知名端口号,这些端口号一般分配在 0-1023之间,我们在编写应用程序时尽量避开这些端口号。

(2)时序分配法:服务器有必要确定监听端口,以让客户端访问服务器上的服务,但是客户端没必要确定端口号。在这种方法下,客户端应用程序完全可以不用自己设置端口号,而是全权交给操作系统进行分配,客户端使用临时端口号,操作系统分配的端口一般大于10000。

3.6  观察端口号

Windows 下使用 netstat -ano 查看所有端口号,netstat -ano|findstr “<端口号>”查看指定端口号。Linux 下可以用 root 用户执行 lsof -i:端口号查看指定端口占用。

lsof -i -U:显示所有打开的 UNIX domain 和端口文件。

我们用的更多的是netstat

netstat -tunlp 用于显示tcp、udp端口和进程的相关情况。

netstat -tnlp | grep 端口号

-t 仅显示tcp相关选项

-u 仅显示udp相关选项

-n 拒绝显示别名,能显示数字的全部转换成数字

-l 仅列出监听的服务状态

-p 显示相应的进程

所以,一般来说,不管一台计算机有多少个网卡,每个网卡都有自己的MAC地址,这个地址是不会变化的。而每个网卡正常工作都会有一个自己的IP地址,该IP地址是可以变化的。每台计算机上不同的应用程序都有自己的端口。然后通过服务器的网卡进行网络通信。

总之,操作系统通过源IP、目的IP、源端口、目标端口、协议类型(协议号)五元组识别网络上的唯一通信。

3.7 一台主机上只能保持最多65535个TCP 连接?

很显然,这个说法是错误的。

1、服务端

我们已经知道网络通信五元组是由过源 IP 地址、目标 IP 地址、协议号(协议类型)、源端口号以及目标端口号构成。现在考察的是 TCP 连接,自然五元组中的协议号已经定下来了,于是网络通信五元组就变化为 TCP 四元组。

那就是说 TCP 连接四元组是由源 IP 地址、源端口、目的 IP 地址和目的端口构成。

很明显当四元组中任意一个元素发生了改变,那么就代表的是一条完全不同的新连接。拿我们常用的 MySQL 举例,假设它的 IP 是 X,端口 3306。用户 A 基于 IP 地址 A1,端口 PA 连接 MySQL ,于是构成了一个 TCP 连接四元组(A1,PA,X,3306)。用户 B 基于 IP 地址 B1, 端口 PB 连接同一个 MySQL,这个时候 MySQL 需要开启一个新端口来和用户 B 通信吗?从我们日常的开发就可以知道,MySQL 并不需要这么做,所以用户 B 就和 MySQL 构成了一个新的 TCP 连接四元组(B1,PB,X,3306)。

服务端理论上能达成的最高并发数量是多少?从我们上面的用户 A 和用户 B 构成的 TCP连接四元组:

(A1,PA,X,3306)

(B1,PB,X,3306)

可以看到目的 IP 地址和目的端口(X,3306)是不变的,这样就只剩下源 IP 地址、源端口是可变的。IP 地址是一个 32 位的整数,所以源 IP 最大有 2 的 32 次方这么多个。 端口是一个 16 位的整数,所以端口的数量就是 2 的 16 次方。2 的 32 次方(ip 数)× 2 的 16 次方(port 数)大约等于两百多万亿。所以理论上,我们每个 server 可以接收的连接上限就是两百多万亿。

当然实际上做不到,目前工程实践中可以达到的连接数在千万级别。

2、客户端

前面我们已经说过,“客户端应用程序完全可以不用自己设置端口号,而全权交给操作系统进行分配”,可用的端口号只有 6 万多,从这个角度考虑,客户端最多只能发起 6 万多条 TCP 连接。但其实也不是。

从 TCP 连接四元组来考虑:源 IP 地址、源端口、目的 IP 地址和目的端口,目的 IP 地址和目的端口指的是服务器的 IP 和端口,源 IP 地址、源端口自然就是客户端的。

只要服务器的 IP 或者端口不一样,即使客户端的 IP 和端口是一样的。这个四元组也是属于一条完全不同的新连接。比如:

连接 1:客户端 IP 10000 服务器 IP 10000

连接 2:客户端 IP 10000 服务器 IP 20000

虽然客户端的 IP 和端口完全一样,但由于服务器侧的端口不同,所以仍然是两条不同的连接。问题来了,客户端同一个端口可以连接不同的服务器吗?答案是可以的。

客户端只要启动时不显示绑定到某个端口上,内核是可以使用一个端口连不同的服务端,内核会自己进行选择并恰当地复用的,而且完全不会产生数据混乱,因为“源 IP 地址、目标 IP 地址、源端口号以及目标端口号就能唯一性确定一个 TCP 连接”。

那么对客户端来说,四元组里有 3 个可变,自然客户端能同时支持的连接数比服务器还要大得多。

四、TCP的特性

1、TCP是面向连接的通信协议,通过三次握手建立连接之后才能进行端到端的读写,通讯完成之后,要拆除连接,由于是面向连接,所以只能用于端到端的通信。

2、TCP提供一种可靠的数据流服务,数据可能被拆分后发送,采用超时重传以及应答确认机制是组成TCP可靠传输的关键设计。

3、超时重传的时间选择是动态的,需要采样一个数据包从发送端到接收端回复这段时长来动态设置重传超时值,这个时长就是RTT(Round-trip-time),然后再根据这个RTT通过算法和公式平滑RTT值后,最终确认重传超时值。

4、IP层发送数据,并不能保证数据按照发送的顺序到达目的服务器。当目的服务器IP层接收到数据之后,往上传送到TCP层后,TCP层对包进行排序和错误检查,TCP数据包中包括序号和确认。所以未按照顺序发送的包可以被排序,损坏的包可以被重传。

5、TCP采用滑动窗口机制进行流量控制,所谓的窗口实际表示接收能力,用于限制发送方的发送速度。

6、TCP允许在一个连接上,通信双方可以互相发送数据,就是所谓的全双工通信。

五、TCP的三次握手

5.1 三次握手过程

所谓的三次握手,是指建立一个TCP连接需要客户端和服务端总共发送三次包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,在网络通信中,我们称发起连接的一方为客户端,接收连接的一方为服务端。

(1)第一次握手(请求服务端建立连接):客户端发送标志位SYN=1,以及请求报文的SEQ_NO=J(J是一个随机值)到服务端,客户端进入SYN_SENT状态,等待服务端确认。

(2)第二次握手(服务端确认建立连接,并请求客户端建立连接):服务端发送标志位SYN=1,以及确认位ACK=1,以及应答报文ACK_NO=J+1,同时应答报文添加一个SEQ_NO=K(K是一个随机值),将包发送给客户端已确认连接请求,服务端进入SYN_RCVD状态。

(3)第三次握手(客户端确认建立连接,并反馈给服务端):客户端收到服务端连接请求之后,检查ACK_NO是否为J+1,ACK是否为1,如果正确,则将第三个报文的标志位设置为ACK=1,同时ACK_NO=k+1,并将数据发送给服务端,服务端检查ACK是否为1,ACK_NO是否为K+1,如果是则建立连接,客户端和服务端进入ESTABLISHED状态,完成三次握手。

5.2 为什么三次握手

三次握手的过程是通信双方互相告知序列号的初始值,并确认对方已经收到了序列号的初始值的必经步骤。

如果是两次握手,至多只有客户端知道服务端确认了自己的序列号初始值,而服务端并没有确认客户端的序列号初始值。

三次握手就可以互相确认,因此也不需要四次握手。

5.3 三次握手的漏洞-SYN洪泛攻击

三次握手的过程中有一个缺陷,被成为SYN洪泛攻击。在第二次握手的过程中,服务端应答确认是需要客户端IP地址的,而且因为握手过程没有完成,操作系统会使用队列来维持这个状态(这个队列的大小可以通过参数/proc/sys/net/ipv4/tcp_max_syn_backlog设置)。于是攻击者可以伪造IP,往服务器疯狂发送第一次握手内容,而服务端忙于进行第二次握手,但是第二次握手不会有应答(因为是伪造IP),所以导致服务器队列满,从而拒绝正常连接。

面对这种攻击,有以下解决方案,最好的方案是防火墙

(1)无效链接监视释放。

这种方法不停监视所有的连接,包括三次握手的,还有握手一次的,反正是所有的,当达到一定(与)阈值时拆除这些连接,从而释放系统资源。这种方法对于所有的连接一视同仁,不管是正常的还是攻击的,所以这种方式不推荐。

(2)延缓TCB分配方法

一般的做完第一次握手之后,服务器就需要为该请求分配一个 TCB(连接控制资源),通常这个资源需要 200 多个字节。延迟 TCB 的分配,当正常连接建立起来后再分配 TCB ,则可以有效地减轻服务器资源的消耗。

(3)防火墙

防火墙在确认了连接的有效性后,才向内部的服务器(Listener)发起 SYN 请求,

六、TCP的四次挥手

6.1 四次挥手过程

四次挥手,即终止TCP连接。就是指断开一个TCP连接,需要客户端和服务端共发送4个包以确认连接的断开。

(1)某个应用进程调用active close(主动关闭)。该端的TCP发送一个FIN=1,表示数据发送完毕。应用进程进入FIN-WAIT-1(终止等待-1)状态。

(2)接收到FIN的对端,执行passive close(被动关闭),发出确认报文ACK=1,接收端接收到FIN意味着接收端的应用进程在该连接上再无额外数据可接收,接收端进入CLOSE WAIT(关闭等待)状态。这时处于半关闭状态。即主动关闭端已经没有数据要发送了,但是被动关闭端如果要发送数据,主动关闭端依然要接收。这个状态要维持一段时间,也就是CLOSE WAIT状态持续的时间。主动关闭端收到确认报文后进入FIN-WAIT-2状态。

(3)一段时间后,被动关闭端将调用close关闭它的套接字。这导致它的tcp发送一个FIN,表示被动关闭端也没有数据需要发送了。

(4)主动关闭端收到这个最终的FIN之后,发送一个确认ACK的报文,主动关闭端进入TIME WAITED状态。注意此时TCP连接还没有释放,必须经过2*MSL(最大报文寿命)时间后,主动关闭端撤销相应的TCB,才进入closed状态。TIME-WAITED时间一般介于1到4分钟之间。

(5)被动关闭端,只要收到了客户端发送的确认,立马进入closed状态。同样撤销TCB,就结束了这次TCP连接。因此,被动关闭端要比主动关闭端的TCP连接结束的早。

6.2 为什么四次挥手

TCP是全双工的连接,必须两端(两个方向)同时关闭连接 ,连接才算真正的关闭。

如果一方准备关闭写,但它还可以读另一方发送的数据(比如主动关闭端)。发送给 FIN 结束报文给 对方,对方收到后,回复 ACK 报文。当这方也已经写完了准备关闭,发送 FIN 报文,对方回复 ACK。两端都关闭,TCP 连接正常关闭。

6.3 为什么需要TIME-WAIT状态

有以下两个原因:

1、可靠的终止TCP连接。

2、保证让迟来的 TCP 报文有足够的时间被识别并丢弃。

根据前面的四次握手的描述,我们知道,客户端收到服务器的连接释放的 FIN 报文后,必须发出确认。如最后这个 ACK 确认报文丢失,那么服务器没有收到这个 ACK 确认报文,就要重发 FIN 连接释放报文,客户端要在某个状态等待这个FIN连接释放报文段然后回复确认报文段,这样才能可靠的终止 TCP 连接。

在 Linux 系统上,一个 TCP 端口不能被同时打开多次,当一个 TCP 连接处于 TIME_WAIT状态时,我们无法使用该链接的端口来建立一个新连接。

反过来思考,如果不存在 TIME_WAIT状态,则应用程序能够立即建立一个和刚关闭的连接相似的连接(这里的相似,是指他们具有相同的 IP 地址和端口号)。这个新的和原来相似的连接被称为原来连接的化身。新的化身可能受到属于原来连接携带应用程序数据的 TCP 报文段(迟到的报文段),这显然是不该发生的。这是 TIME_WAIT 状态存在的第二个原因

原创文章,作者:速盾高防cdn,如若转载,请注明出处:https://www.sudun.com/ask/76723.html

(0)
速盾高防cdn's avatar速盾高防cdn
上一篇 2024年5月23日 下午1:42
下一篇 2024年5月23日 下午1:48

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注