【快排、UDP/TCP协议、TCP/IP分层模型及每层特点、死锁产生及避免方法】

一、快排

  • 快速排序属于交换排序的一种,是基于二叉树结构的交换排序的方法;
  • 基本思想:

1)任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两个子序列;
2)左子序列中所有元素均小于基准值,右子元素序列中所有元素均大于基准值,然后左右子序列重复该过程,直到所有排列都在相应的位置上;
在这里插入图片描述

代码1:

void quickSort(int left, int right, vector<int>& arr)
{
	if(left >= right)
	{
	  return;
	}
	int i, j, base, temp;
	i = left, j = right;
	base = arr[left];  
	//取最左边的数为基准数
	while (i < j)
	{
	while (arr[j] >= base && i < j)
			{
			    j--;
			}
	while (arr[i] <= base && i < j)
	 		{
	 			i++;
			}
		if(i < j)
		{
			temp = arr[i];
			arr[i] = arr[j];
			arr[j] = temp;
		}
	}
	//基准数归位
	arr[left] = arr[i];
	arr[i] = base;
	quickSort(left, i - 1, arr);//递归左边
	quickSort(i + 1, right, arr);//递归右边
}

代码2:

void QuckSort(int left, int right, vector<int>& v)
{
	if (left >= right)
		return;

	int i, j, base;
	i = left;
	j = right;
	base = v[left];

	while (i < j)
	{
		while (i < j && v[j] >= base)
		{
			j--;
		}
		v[i]=v[j];
		while (i < j && v[i]<=base)
		{
			i++;
		}
		v[j]=v[i];
	}
	v[i] = base;
	//递归左边和右边
    QuckSort(left,i-1,v);
    QuckSort(i+1,right,v);
}

二、UDP/TCP协议

TCP/UDP协议是在传输层负责应用程序之间传输的协议;

1、UDP协议

  • UDP协议的特性

(1)无连接
只需要知道对端地址信息,不需要建立连接就可以发送数据;
(2)不可靠
不保证数据能够到达对端,没有确认机制,没有重传机制,若网络故障则该段无法发送到对方;
(3)面向数据报
面向报文的传输方式是应用层交给UDP多长的报文,UDP就照样发送,既不合并,也不拆分,而是保留这些报文的边界,即一次发送一个报文;

  • UDP协议格式
    在这里插入图片描述

1)16位源端口和16位目的端口
负责应用程序之间数据的传输,表示数据从哪里来到哪里去
2)16位校验和
校验数据一致性(校验发送和接收的数据是否一致),利用的是二进制反码求和算法
在这里插入图片描述

  • 发送方:
    首先是把全零放入校验和字段并且添加伪首部,然后把UDP数据报看成是由许多16位的子串连接起来,若UDP数据报的数据部分不是偶数个字节,则要在数据部分末尾增加一个全零字节(此字节不发送),接下来就按照二进制反码计算出这些16位字的和【超出16位的部分(求和进位部分)取出来再次与低16位进行相加】。将此和的二进制反码写入校验和字段。
    接收方:把收到得UDP数据报加上伪首部(如果不为偶数个字节,还需要补上全零字节)后,按二进制反码计算出这些16位字的和。当无差错时其结果全为1,。否则就表明有差错出现,接收方应该丢弃这个UDP数据报。

(一旦校验和出错,就会直接丢弃)
3)16位UDP长度
表示整个数据报(UDP首部+UDP数据)的最大长度

  • 【以字节为单位,因为UDP报头是8个字节,数据报长度为两个字节存储(65535:2的16次方);
    则udp在使用sendto发送数据的时候,限制其长度不能大于64k-8Byte
  • UDP的缓冲区

(1)UDP没有真正意义上的发送缓冲区,调用sendto会直接交给内核,由内核将数据传给网络层协议进行后续传输动作;
(2)UDP具有接收缓冲区,但是接收缓冲区不能保证收到的UDP报的顺序和发送的UDP报的顺序一致;且如果缓冲区满了,再到达的UDP数据就会丢失;

  • 对于UDP的一些理解

1)在发送大文件时,文件数据大于64k-8,则不能一次性发送,需要用户在应用层进行分包操作,将一个大数据截断为多个小数据进行收发;
2)udp并不保证数据有序到达,因此需要用户在应用层进行包序管理
3)udp的数据传输是整条收发的;
具体理解整条收发:
a、发送方:
udp使用sendto发送数据的时候,将数据放到socket发送缓冲区,socket会直接为这个数据封装udp报头然后发送出去;
b、接收方:
udp使用recvfrom接收数据的时候,人家发送了100个字节,那么这个数据就不能分成两个50字节进行多次接收,若分为两次接收则第二次没有报头,不知道会接收多少;
4)接收缓冲区和重传
使用udp接收数据的时候,接收缓冲区定义要足够大,否则若发送的数据比较大,缓冲区不够,则recvfrom会失败。
应用层进行分包操作,需要程序员自己进行分包;
接收方接收之后要自己整合,若差了一段(重新发送/什么都不做/只传送缺失部分)处理方式由程序员决定;

  • UDP常用场景

UDP常用一次性传输比较少量数据的网络应用,如DNS,SNMP等;
因为对于这些应用,若是采用TCP,为连接的创建,维护和拆除带来不小的开销;
UDP也常用于多媒体应用(如IP电话,实时视频会议,流媒体等)数据的可靠传输对他们而言并不重要,TCP的拥塞控制会使他们有较大的延迟,也是不可容忍的;

2、TCP协议

TCP协议全称为“传输控制协议

  • TCP的特性

(1)面向连接
通过三次握手/四次挥手来实现连接与断开;
(2)可靠传输
保证数据有序并可靠到达对端;
(3)面向字节流
基于连接字节流传输(不限制上层发送/接收数据的大小);

  • 协议格式
    在这里插入图片描述

解释:
(1)16位源/目的端口:表示数据从哪个程序来,到哪个程序去;
(2)32位序号/32位确认序号:用于实现tcp在传输层的包序管理—tcp有序交付数据
(3)hdrl(4位首部长度):表示tcp报头最多有多少个四字节(15*4=60字节)
(4)6位保留位
(5)6位标志位:
URG-紧急指针标志;
ACK-确认回复标志;
PSH-提示立即接收位;
RST-重置连接位;
SYN-连接建立请求位;
FIN断开连接请求位;
(6)16位窗口大小:使用滑动窗口机制进行流量控制,来告诉对端所能发送的最大数据量;
(7)校验和:二进制反码求和—校验数据一致性;
(8)紧急指针:指明哪些数据是紧急数据;
(9)选项数据:三次握手时,协商Mss大小的数据;

  • TCP连接管理

在这里插入图片描述(1)socket()函数
函数作用:创建一个网际字节流套接字
包含头文件:sys/socket.h
函数原型:int socket ( int family, int type, int protocol )
参数说明:
family:指明协议族( 如AF_INET,AF_INET6,AF_LOCAL等 );
type:指明套接字类型( SOCK_STREAM,SOCK_DGRAM等 );
protocol:直接指明某种 family 和 type 的组合而无视前2个参数,如下图( 如果该参数为0则启用family和type的组合类型 );

返回值说明:成功返回描述符,失败返回-1;
(2)bind()函数

函数作用:将套接字与特定的IP地址和端口绑定起来;
函数原型:int bind ( int sockfd, const struct sockaddr * myaddr, socklen_t addrlen )
参数说明:
sockfd:要绑定的套接字
myaddr:要绑定的套接字地址结构
addrlen:上述结构的大小
返回值说明:若成功返回0,失败返回-1。
特别说明:一般情况,服务器绑定一个INADDR_ANY( 任意型 )的地址就可以了,客户端不用绑定。
(3)shutdown()函数【并不释放套接字资源】
函数原型:Shutdown(fd,how);
how类型:SHUT_WR(关闭写)/SHUT_RD(关闭读)/SHUT_RDWR(关闭读写)
(4)close(fd):关闭读写并释放资源
(5)FIN/ACK/SYN等都是发送数据包的时候将标志位置位为相应标志;【且FIN包并不是关闭套接字的时候才会发送,只关闭了读或者写端也会发送FIN包】
(6)若用户直接调用close(fd)释放资源,还需要第四次挥手吗?
答:第四次挥手是在内核进行的,所以还是会进行,除非断电断网;
(7)连接的建立,握手为什么不是两次/四次而是三次?
答:因为两次不安全,四次没必要;
-TCP是双向通信,需要双方都保持在线,具有数据收发能力;
如果客户端发送SYN请求后直接退出了,那么服务端发送的数据会丢失,所以既要发送ACK也要发送SYN;
-四次没有必要,完全可以把ACK和SYN合并发送;
(8)挥手为什么是四次而不是三次?(为什么不能合并ACK和FIN?)
因为用户向对端发送FIN包,不仅有close(关闭读写释放资源)也有可能是仅关闭了写端发送的FIN,意味着该端有可能还要接收数据;
因此操作系统收到FIN,不能直接断开,
接收方的ACK和FIN包不能一起发送,只有接收方确认不再发送数据了才会发送FIN包。
(9)FIN包发送方为什么发送最后一个ACK后并没有进入CLOSE状态释放资源,而是进入TIME_WAIT状态等待一段时间?
答:假设若是没有TIME_WAIT直接进入CLOSED释放资源有可能造成危险;

  • 客户端发送了ACK后,若这个ACK丢包了,服务端将收不到ACK,因此超时后会向客户端重传FIN包; 若客户端又立即使用相同的地址信息启动了客户端,什么也没干突然收到一个FIN,则会对新连接直接造成影响;
  • 若客户端又立即使用相同的地址信息启动了客户端,向服务端发送SYN,而服务端这时候处于LAST_ACK,收到SYN则会认为状态错误,向客户端发送RST重置连接报文,要求对方重新建立连接;
    所以主动关闭方,发送最后一个ACK之后,需要等待一段时间,处理有可能重传的FIN包;(等待的这段时间,时常是两个MSL时间----MSL-报文最大生存周期)
    (10)如果重传后,但最终两次都到达了,会怎么办?
    答:如果第一次的到达了,tcp会去重,如果收到第一个,它会释放资源退出,则第二个没有机会被处理,就结束了;
    (11)如果三次握手失败了,服务端会如何处理?
    答:a)客户端发送的SYN直接就没有到达服务端----服务端不知道有这个请求,因此没什么处理的;
    b)客户端发送了SYN后服务端收到并进行了SYN+ACK响应,但是没有收到客户端的ACK;服务端等待最后一个ACK超时后,则会给客户端发送RST请求重置连接报文,要求对方重新发送连接请求,释放当前新建的这个套接字,而并非重传SYN+ACK;
    (12)服务器上出现大量CLOSE_WAIT是什么原因?
    答:服务器代码出现问题;
    CLOSE_WAIT—收到FIN进行回复后的状态,等待程序调用close/shutdown(wr)
    即没有给对方发送FIN包,关闭写或读写端,释放资源(没有释放资源);
    表示程序中可能对大量套接字连接断开后,没有调用close关闭,释放资源—这是一种代码中的操作
    (13)服务端出现大量TIM_WAIT为什么?
    答:TIM_WAIT----主动关闭,发送最后一个ACK之后的状态;意味着服务器上大量主动关闭连接----通常出现在爬虫服务器上;
    解决方法:可以开启地址重用/设置MSL时间;
  • TCP用于保证可靠传输的方式?

(1)连接管理:通过连接管理保证双方都具有数据收发能力;
(2)确认应答机制:发送的每一条数据,都要求对方进行确认回复(通过协议字段中标志位ACK实现)
(3)超时重传机制
发送数据后一段时间内都没有收到确认回复,则认为数据丢失,进行重传;
(4)协议中校验和字段:通过协议字段中的校验和校验数据一致性;若不一致则要求对方进行重传;
(5)协议中序号和确认序号字段:进行包序管理,保证交付数据包序有序;

  • TCP的确认应答机制

在这里插入图片描述
seq—当前数据起始序号;
ack–确认序号–确定这个序号之前的数据都已经完整到达;
即若发送端连续发送多条数据,收到的每一个确认回复,都保证其确认序号之前的数据都已经安全到达了;
这样的话就算某个确认回复丢失了,也不会影响后续数据的确认回复,则会认为这条数据也安全到达了—避免因为确认回复丢失而导致重传

  • TCP超时重传等待时间

等待时间是动态的,随着网络变化的;一开始等待很短的时间就会重传。但是若多条数据都在短时间内无法得到响应,则会调整等待时间;

  • TCP流量控制、防止丢包

滑动窗口机制
a)实现发送发连续发送多条数据,并且进行流量控制—借助协议中的窗口大小字段实现
b)三次握手的时候,通信双方会通过选项数据协商MSS(最大数据段大小)一条报文中的数据最大大小;(两方协商选择较小的一个)
在这里插入图片描述

  • 发送方:通过维护一个窗口后沿窗口前沿加上当前发送位置来维护一个窗口,表示有多少数据当前可以连续发送;
  • 接收方:根据自己的接纳缓冲区剩余空间大小,使用窗口后沿和窗口前沿,当前接收位置维护一个接收窗口;( 从哪里开始接收,接受多少,接收的数据放在缓冲区的什么位置)
    -发送、接收双方的前后沿移动问题;
    答:
    1) 发送方后沿移动:取决于是否收到后沿数据的确认回复;
    2)发送方前沿的移动:取决于收到的确认中窗口大小;因为前沿减去后沿不能大于窗口大小;
    3)接收方后沿移动:取决于是否收到后沿数据;
    4)接收方前沿移动:取决于接收缓冲区中剩余空间大小(取决于用户是否通过recv将数据从缓冲区取出去了)
    5)当后沿到达了前沿。
    对于发送方:接收方返回的窗口为0,并且发送的数据都已收到回复;
    对于接收方:接收缓冲区中数据已经满了;
  • Tcp是双向通信,意味着发送方也可能是接收方,既有发送缓冲区,也有接收缓冲区;因此每一端口都会维护自己的发送窗口和接收窗口;

-TCP基于滑动窗口实现的快速重传机制

在这里插入图片描述
存在问题:若连续发送多条数据,前边的数据丢失,则会导致接收端不再进行确认回复
因为每一条确认回复,都要保证之前的数据已经到达;第一条数据丢失因此后边的数据收到也不能进行回复,只能等待发送方超时后重传这些数据,并且超时等待时间较长;
常规处理方式:若没收到第一条数据,而收到了第二条数据,则接收方有理由认为第一条数据丢失了,因此会每一小段时间给发送方发送第一条数据重传请求,连续发送三次,发送方连续收到三次同一条数据的重传请求,则对这条数据进行重传;

  • 为什么是三次:因为要防止数据延迟的情况;
    -数据丢失后到底重传哪些数据?
    答:a)针对网络状况不好的情形,收到回复才会发送下一条数据(停等协议);
    b)选择重传:哪条丢失传哪条
    c)回退n步协议:从丢失的那条开始,全部重传;
    拥塞窗口
  • 功能:
    滑动窗口机制实现了数据的连续大量发送;但是若在通信时,不了解网络状况直接发送大量数据,就有可能大量的丢包重传;
    因此发送方总是维护一个拥塞窗口进行拥塞控制,实现网络探测避免因网络不好造成的大量丢包
  • 控制过程:开始发送数据的时候,定义拥塞窗口大小为1,每次收到一个ACK应答,拥塞窗口加1;
    每次发送数据的时候,将拥塞窗口接收主机反馈的窗口大小做比较,取较小值作为实际发送的窗口;
  • 阈值的变化
    TCP开始启动的时候,阈值等于窗口最大值;
    每次超时重传的时候,阈值变为原来的一半,同时拥塞窗口回置1;
  • 注:因为这样增长拥塞窗口将会增长非常快,所以引入一个阈值,当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长;
    少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞
  • 提高性能的几个机制

(1)延迟应答机制
目的:为了提高传输性能
功能:接收方接收到一条数据,并不立即进行确认回复,因为立即进行回复,会造成窗口变小;造成吞吐率降低;因此延迟一会进行应答,这个延迟期间有可能数据被recv出去,窗口不变/变大保证吞吐率;
(2)捎带应答机制
每一条确认回复都是一条数据,至少需要一个tcp报头;
为了减少这种纯确认回复报头的数据,因此就会将这次的确认回复和即将要发送的数据合成一个数据进行发送

  • TCP的面向字节流

创建一个socket同时会在内核中创建一个发送缓冲区和一个接收缓冲区
1)调用write时,数据会先写入发送缓冲区中;
如果发送的字节数太长,会被拆分成多个TCP数据包发出;
如果发送的字节数太短,数据会在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适时机发送出去;
2)接收数据过程中,也是会到达内核的接收缓冲区,然后程序调用read从接收缓冲区拿数据;
3)对于TCP,既有发送缓冲区,又有接收缓冲区,将其称为全双工
4)因为缓冲区的存在,可以依次write100个字节,也可以调用100次,每次写一个字节;
面向字节流传输:传输比较灵活,并不限制上层发送数据的大小以及接收的数据大小;

  • TCP粘包问题

1)即多条数据粘连在一起被tcp作为一条数据进行发送或者交付给上层,将这种现象称为粘包
2)本质原因:tcp对上层边界数据不敏感(不关心上层是什么数据,只管从缓冲区取出合适大小的数据进行发送
3)解决方案:
(因为在传输层,并不对数据进行数据边界管理,所以需要程序员在应用层自己进行边界管理)
a)使用特殊字符作为间隔(比如http协议)
b)数据定长—(会造成资源浪费)
c)在应用层头部定义一数据长度(UDP协议就可以定长)

  • TCP异常问题

(1)进程终止:进程终止会释放文件描述符,仍然可以发送FIN和正常关闭没有什么区别;
(2)机器重启:和进程终止的结果相同;
(3)机器掉电或者网线断开:接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset.
使没有写入操作, TCP自己也内置了一个保活定时器, 会定期询问对方是否还在. 如果对方不在, 也会把连接释放.

  • TCP小结

(1)可靠性
校验和(验证发送与接收的信息是否一致)
序列号和确认序列号(用于包序管理)
连接管理
确认应答
超时重传
流量控制
拥塞控制
(2)提高性能
滑动窗口
快速重传
延迟应答
捎带应答
(3)其他
定时器(超时重传定时器,保活定时器,TIME_WAIT定时器)

3、TCP/UDP对比

(1)TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;
(2)UDP用于对高速传输和实时性要求较高的通信领域,例如视频传输等;

三、TCP/IP模型分层及每一层的特点

(1)早先出现的是OSI(开放系统互连)七层模型,将网络划分为了七层;
划分:
应用层、表示层、会话层、传输层、网络层、数据链路层、物理层;
优点:将服务、接口和协议三个概念明确区分,理论完整,通过七层网络模型可以实现不同系统不同网络之间的可靠通讯
缺点:复杂不实用;
(2)演变进化的TCP/IP协议簇五层模型
TCP/IP五层模型:应用层;传输层;网络层;链路层;物理层;
每一层上的作用及代表协议:

  • 应用层:负责应用程序之间的数据沟通
    典型协议:http(超文本传输协议)/ftp(文件传输协议)/smtp(电子邮件传输)
    举例:qq是一个应用程序 qq与qq之间进行通信使用的协议;应用程序是程序员编写的,因此应用层协议也是程序员自己写的
  • 传输层:负责两台主机之间的数据传输
    典型协议: tcp/udp
    举例:qq发送的数据保证是发给qq的。而不会发送给其他程序—端口—传输层的协议都会包含端口信息;
  • 网络层:负责地址管理与路由选择
    典型协议:ip协议
    典型设备:路由器(实现数据的路由转发)
    举例:
    为每一条网络通信数据从起点到终点主机之间选择合适的路径进行传输—ip—网络层的协议都会包含有ip地址信息;
  • 链路层:负责相邻设备之间的数据帧传输与识别
    典型协议:以太网协议
    典型设备:交换机
    举例:通过mac地址标志结点设备与节点设备之间的通信路径;
    (mac地址是用来识别数据链路层中相连的节点,长度为48位,在网卡出产时就确定了,不能更改)
    (3)数据传输流程举例:
    在这里插入图片描述

四、死锁的产生及避免方法
1、死锁的产生

(1)什么是死锁?
一组进程中,每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么该组进程是死锁的;
(2)死锁的四个必要条件?
a)互斥条件:
某段时间内某资源只能被一个进程占用;
b)请求和保持条件
进程已经保持了至少一个资源,但是又提出了新的资源请求,该资源被其他进程占有,故此进程阻塞;
c)不可抢占条件:进程已经获取的资源在使用完以前不能被抢占;
d)循环等待条件:死锁时必然会有进程等待资源的循环链;
2、死锁的处理方法
a)预防死锁
破坏死锁产生的四个必要条件中的一个或几个,来进行预防
b)避免死锁
资源分配动态过程中,防止系统进入不安全状态(没有安全序列则系统处于不安全状态)
常用银行家算法来避免死锁

c)检测死锁:允许产生死锁,但可以很快检测出来,并采取相应措施;
d)解除死锁:撤销进程,回收资源等方法;
四种方法对比:对死锁的防范程度逐渐减弱,但是对资源的利用率提高了进程的并发度也提高了

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页