赞
踩
博客主页:花果山~程序猿-CSDN博客
关注我一起学习,一起进步,一起探索编程的无限可能吧!让我们一起努力,一起成长!
目录
嗨!收到一张超美的风景图,愿你每天都能顺心!
如今,TCP/IP协议,使用“源IP” ,"目的IP",“源端口号”,“目的端口号”,“协议号”这五个组来标记一个网络服务。
如下,就是体现在同台机器上,web浏览器打开多个页面,端口号不同;不同机器,源IP不同的例子:
端口号在网络中是用来标识一台机器中的一个特定通信的。问?一个进程是否能绑定多个端口号?
答:当然,比如上面web的cline多开网页,操作系统为其分配新端口号,而一个端口号不能代表多个网络通信,不然操作系统无法正确将数据信息向应用层交付。
cat /etc/services
1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的(我们网上买的服务器大多默认不开放端口号,需要我们手动打开)
sudo 提升权限后,我们可以详细看到服务的进程名。
常用使用方法:pidof [进程名] | xargs kill -9 (命令解释,xargs 将标准输入内容转化为指令行参数)
在我们使用套接字应用时,我们为什么经常使用 uint16_t类型? 答:因为传输层使用的TCP/UDP协议要求的。
问?在传输层是如何实现向上交付,于向下交付?
答:向下交付:我们只需上层协议发送的数据,字符串拼接形成报头。
向上交付:提取目的端口号,然后从8字节开始提取有效载荷,最后操作系统完成信息分配。
本质上是一个结构体(位段) ,当需要获取报文里面的数据时,只需要进行数据强转为udp结构体类型,就能正确的读取每个数据。
无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接;不可靠: 简单,没有确认机制, 没有重传机制; 如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息;面向数据报: 不能够灵活的控制读写数据的次数和数量( 报文不能拆分,也不能合并,发多少收多少,发几次,也需要收几次);
全双工,就是既能发送信息,同时又能接收信息,两者不冲突。半双工发送与接收信息两者互斥则不允许。
全双工的特点:发送与接收没有使用公共缓冲区,比如:UDP发送是直接交给OS发送,接收有另一个专门的缓冲区接收。
向下交付:我们在学习udp时发现,报头的本质是一个位段类型,传输层接收到应用层传递的tcp报头数据(设置这个结构体的数据,后拷贝)后,为数据添加报头后向下进行交付。
向上交付:如下图,我们得先理解4位首部长度
我们知道UDP协议不关心,数据报文是否被收到,可靠性不高。反过来tcp协议具有可靠性,需要关心数据是否被收到,因此某端在成功接收到数据报后,需要通过一种方法返回已收到信息。
先由下图,逐步引出对tcp的理解:
序号与确认序号的5层理解:
1. 将请求与应答一一对应。
2. 确认序号的含义:表示前面的数据已经安全接收。如果C端接收到了3001的确认序号,则代表3000,2000,1000已经完整接收。
3. 允许部分确认丢失,或者不应答。假设C端收到3001,前面的完整接收,所以C端不会选择重发;如果C端传递时丢失2000,当S端收到3000,1000而没有2000时,3001将不会被设置,而1001将会被设置(只收到1001),这就是不应答。
4. 为什么要用两个序号,而不是一个数字?(小面试) 任何一方通信都是全双攻的,各自也需要保证各自的信息是否传达成功,接收方也会携带自身的数据。
5. 保证数据按序到达。乱序是一种不可靠,提取数据是按照顺序来的,而有了序号,OS就会对资源进行排序,保证数据按序到达。
两序号作用:保证了确认应答,和数据排序。
本质上是一种流量控制,在上一小章中,我们知道UDP没有发送端缓冲区,接收端有缓冲区,那tcp是否有缓冲区呢?
答:有接收,发送缓冲区。数据在应用层,进行send,recv后数据将首先存放在系统中的发送,接收缓冲区。
- 发送缓冲区:TCP协议会将数据分割成多个数据包,如果网络拥塞或接收方接收数据速度慢,发送缓冲区可能会满,此时发送方会等待接收方确认已接收的数据,然后继续发送数据。
- 接收缓冲区:如果接收缓冲区满了,接收方会通知发送方暂停发送数据,直到接收方处理完缓冲区中的数据。
那如何让发送方知道,接收方缓冲区满了?
16位窗口大小:标记当前tcp连接所剩余的缓冲区大小。
每个报文都有其自身所携带的信息,他们会被分成6类,我们可以将6个标记位理解为报文类型。
关于6个标记位的认识,我们通过tcp连接管理机制来学习吧(三次握手,四次挥手)
1.三次握手
首先我们需要理解:服务端管理相应的连接是需要资源的(内存 + cpu)
服务端对大量客户端连接的管理,必然是先描述,后管理。一个客户端的连接,服务器必然会为其创建合适的数据类型,然后再用数据结构进行管理。
示意图:
上图示意:第一次客户端向服务端发送SYN被设置的完整报头,服务端也会发送SYN+ACK被设置的报头,并且会进入SYN_RCVD的状态,最后等待客户端发送来的ACK报头。
理解:为什么是三次握手,而不是一次,2次或者是4次?
答:
1. 如果是一次请求,服务端就会为客户端进行创建对象,那一台简单的客户端循环发出请求,服务端的资源就很容易消耗完,这种攻击叫做SYN洪水。
2. 如果是4次请求?我们知道服务端是作为大量客户端请求相应的处理中心,在前三步已经达成连接后,还通知客户端,这一步是没有意义的。
3. 2次为什么不可以? 服务端对客户端进行请求应答了,为什么服务端还需要等待客户端进行应答? 在服务端进入通信状态时,先让客户端进入通信状态,这样只有在客户端都准备完成后,服务端才工作,这滞后性,变相的提高了服务端工作效率。
4. 并且3次握手不一定成功。服务端建立连接后并不会向客户端进行连接成功的应答。
2.四次挥手
有那些情况下,TIME_WIAT会导致bind失败?
- 服务器需要处理非常大量的客户端的连接(每个连接的生存时间可能很短, 但是每秒都有很大数量的客户端来请求)。
- 这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃, 就需要被服务器端主动清理掉), 就会产生大量TIME_WAIT连接。
- 由于我们的请求量很大, 就可能导致TIME_WAIT的连接数很多, 每个连接都会占用一个通信五元组(源ip,源端口, 目的ip, 目的端口, 协议). 其中服务器的ip和端口和协议是固定的。如果新来的客户端连接的ip和端口号和TIME_WAIT占用的链接重复了(短时间内重复登录), 就会出现问题。
- 服务端异常崩溃
而为了解决上面场景,编写套接字时使用setsockopt()设置属性 选项SO_REUSEADDR为1, 表示允许创建端口号相同,但IP地址不同的多个socket描述符。
功能:URG被设置的报文,TCP会优先提取紧急指针(偏移量)所标示的数据范围,优先提交应用层。
理解:我们知道数据都是客户端依次发出的,tcp协议会先将大量数据进行排序,丢失的申请重发(数据交付是要排队的),而URG设置后报文的部分数据则会优先被交付(俗称插队)不过这个多出现在服务端的管理。
TCP协议中会对应用层载入的数据,进行一个字节一个字节的标记,说白就是char数组下标+1,这也是上面说过的序列号。
功能:每一个ACK都带有对应的确认序列号,意思是告诉发送者,我已经收到了哪些数据,下一次你从哪里开始发。
就是发送端从发送数据后开始计时,等到达一定时间后,未收到ACK则重新发送。
情况一:ACK丢失
主机B的ACK丢失,但主机B已经缓存了数据,主机A在下一个ACK接收时超过特定时间,主机A会触发超时重传,重新发送,
这时,主机B就有可能会收到大量的重复数据(不过主机B会去重),接收到后返回ACK。这就是一次超时重传。
网络情况比较复杂,在不同的网络环境中,超时时间的设置也不同,那如何动态设置超时时间?
答:Linux中(BSD Unix和Windows也是如此), 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍,第二次重发2 * 500ms,第三次重发3* 500ms,当积累到一定次数后,TCP会认为网络或者主机出现问题,强制关闭连接。
从上面的确认应答机制,我们可以发现一些问题,发送端发送一个报文,会等待ACK返回,然后再向接收端发送下一个内容。效率太低了,那有什么方法来提高效率?
首先了解一下窗口:窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。
理解:
可能存在的异常情况:
情况一:数据包接收,ACK丢失
这种情况一般不会有影响,因为确认序号是当前序号的前面序号已经全部接收,因此下一个ACK将会确认。
情况二:数据包丢失
主机B在接收主机A连续发送的多个数据报中,首先将所收到的数据载入接收缓冲区中,排序检查发现缺失1001~2000,因此后面所返回的ACK的确认序号会全部设置为1001,当主机A收到连续3个同样的ACK确认序号为1001,这就会触发重发机制,滑动窗口会定位该数据段,重新发送。
这种机制被称为:“高速重发机制”(也叫快重传),但这要求主机A连续收到3次相同的ACK。
如何理解同时存在快重传与超时重传?
比如下面情况:
理解:1、接收端可以通过ACK,在报文中窗口大小设置接收缓冲区剩余量的大小,让发送方动态设置滑动窗口的大小,如果窗口大小为0,那么滑动窗口设置为0,也就是停止发送数据。2. 在第一次发送方向接收方发送信息时,为防止数据溢出,发送方会定期发送询问窗口大小的数据段,动态设置滑动窗口。
当我们发现少量丢包时,我们可能会触发超时重传(或者快重传);但如果遇到大量丢包呢?这就是网络拥塞了,TCP协议则会立即停止重传。
在学习网络拥塞前,我们需要以更大的视角,网络中分布着大量的用户端,服务端。
意义:当TCP通信开始后, 网络吞吐量会逐渐上升; 随着网络发生拥堵, 吞吐量会立刻下降; 拥塞控制, 归根结底是TCP协议想尽可能快的把数据传输给对方, 但是又要避免给网络造成太大压力的折中方案。
当接收方接收到发送的信息后,并不立即发送ACK,而是等待200s让应用层将数据取走,如果立即发送ACK下次窗口大小为500K, 而等待后下次窗口大小则为1mb,这样通过延迟应答机制,窗口变大,吞吐量增大,吞吐量增大,传输效率提高。
- 数量限制:每隔N个包,延迟应答一次
- 时间限制:超过延迟应答的时间立即应答一次。
tcp传输控制协议之所以这么复杂,是既保证了可靠性,又尽可能的提高了效率。
保证可靠传输:
保证效率:
我们知道TCP协议接收方从接收缓冲区中读取数据时,数据包之间是不像UDP那样一个一个地读取,而是一段一段的读取,有可能到的内容存在1.5个数据包。问题是我们怎么解决这个数据包之间的分界问题?
答:这个是应用层的事情,TCP只负责数据在将数据发出,收到的数据拷贝到接收缓冲区中,数据包分界tcp不关心。
应用层常见的区分法:
1. 定长数据包,由于TCP没有区分数据包,通过read()读取相应的数据包,在应用层提前声明数据包的长度。
2. 分割符,比如设置"\r\n****\r\n"来区分数据包。
3. 自定义描述 + 分割符, 比如我们曾经的“length\r\n*****\r\n”来动态分割数据包
对于一些长连接的服务,例如QQ聊天,客户端如果检测到长时间未进行数据发送,服务端的资源任然占用着,则会在客户察觉不到的情况下关闭连接,在客户需要发送时,会再次连接。
backlog:指定在拒绝新连接之前,操作系统可以排队等待的最大已连接数量。
所以accept是否参加了三次握手?? 答:并没有,accept只是对已经连接的进行调用获取他们的套接字。
一旦新的连接请求时,由于已经超过了等待的最大连接数, 因此服务器,不会发出SYN + ACK(不同意连接),并将此次连接信息载入半连接队列(未连接成功的),在netstat上显示此次连接为 SYN_RECV 状态, 而不是 ESTABLISHED 状态。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。