一 网络各层有哪些协议
1.物理层(Physical Layer)
OSI模型的最低层或第一层,规定了激活、维持、关闭通信端点之间的机械特性、电气特性、功能特性以及过程特性,为上层协议提供了一个传输数据的物理媒体。在这一层,协议数据单元为比特(bit)。在物理层的互联设备包括:集线器(Hub)、中继器(Repeater)等。
2.数据链路层(Datalink Layer)
OSI模型的第二层,它控制网络层与物理层之间的通信,其主要功能是在不可靠的物理介质上提供可靠的传输。该层的作用包括:物理地址寻址、数据的成帧、流量控制、数据的检错、重发等。在这一层,协议数据单元为帧(frame)。在数据链路层的互联设备包括:网桥(Bridge)、交换机(Switch)等。
3. 网络层(Network Layer)
OSI模型的第三层,其主要功能是将网络地址翻译成对应的物理地 ,并决定如何将数据从发送方路由到接收方。该层的作用包括:对子网间的数据包进行路由选择,实现拥塞控制、网际互连等功能。在这一层,协议数据单元为数据包(packet)。在网络层的互联设备包括:路由器(Router)等。
4.传输层(Transport Layer)
OSI模型中最重要的一层,是第一个端到端,即主机到主机的层次。其主要功能是负责将上层数据分段并提供端到端的、可靠的或不可靠的传输。此外,传输层还要处理端到端的差错控制和流量控制问题。在这一层,协议数据单元为数据段(segment)。传输层协议的代表包括:TCP、UDP、SPX等。
5.会话层(Session Layer)
OSI模型的第五层,管理主机之间的会话进程,即负责建立、管理、终止进程之间的会话。其主要功能是建立通信链接,保持会话过程通信链接的畅通,利用在数据中插入校验点来同步两个结点之间的对话,决定通信是否被中断以及通信中断时决定从何处重新发送。
6.表示层(Presentation Layer)
OSI模型的第六层,应用程序和网络之间的翻译官,负责对上层数据或信息进行变换以保证一个主机应用层信息可以被另一个主机的应用程序理解。表示层的数据转换包括数据的解密和加密、压缩、格式转换等。
7.应用层(Application Layer)
OSI模型的第七层,负责为操作系统或网络应用程序提供访问网络服务的接口。术语“应用层”并不是指运行在网络上的某个特别应用程序,应用层提供的服务包括文件传输、文件管理以及电子邮件的信息处理。在应用层的互联设备包括:网关(Gateway)等。典型设备:应用程序,如FTP,SMTP ,HTTP。
二 TCP三次握手
第一次握手:客户端请求建立连接,向服务端发送一个同步报文(SYN=1),同时选择一个随机数 seq = x 作为初始序列号,并进入SYN_SENT状态,等待服务器确认。
第二次握手:服务端收到连接请求报文后,如果同意建立连接,则向客户端发送同步确认报文(SYN=1,ACK=1),确认号为 ack = x + 1,同时选择一个随机数 seq = y 作为初始序列号,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务端的确认后,向服务端发送一个确认报文(ACK=1),确认号为 ack = y + 1,序列号为 seq = x + 1,客户端和服务器进入ESTABLISHED状态,完成三次握手。
为什么需要三次握手,而不是两次?
1.防止已过期的连接请求报文突然又传送到服务器,因而产生错误和资源浪费。
在双方两次握手即可建立连接的情况下,假设客户端发送 A 报文段请求建立连接,由于网络原因造成 A 暂时无法到达服务器,服务器接收不到请求报文段就不会返回确认报文段。
客户端在长时间得不到应答的情况下重新发送请求报文段 B,这次 B 顺利到达服务器,服务器随即返回确认报文并进入 ESTABLISHED 状态,客户端在收到 确认报文后也进入 ESTABLISHED 状态,双方建立连接并传输数据,之后正常断开连接。
此时姗姗来迟的 A 报文段才到达服务器,服务器随即返回确认报文并进入 ESTABLISHED 状态,但是已经进入 CLOSED 状态的客户端无法再接受确认报文段,更无法进入 ESTABLISHED 状态,这将导致服务器长时间单方面等待,造成资源浪费。
2.三次握手才能让双方均确认自己和对方的发送和接收能力都正常。
3.告知对方自己的初始序号值,并确认收到对方的初始序号值。
TCP 实现了可靠的数据传输,原因之一就是 TCP 报文段中维护了序号字段和确认序号字段,通过这两个字段双方都可以知道在自己发出的数据中,哪些是已经被对方确认接收的。这两个字段的值会在初始序号值得基础递增,如果是两次握手,只有发起方的初始序号可以得到确认,而另一方的初始序号则得不到确认。
为什么要三次握手,而不是四次?
因为三次握手已经可以确认双方的发送接收能力正常,双方都知道彼此已经准备好,而且也可以完成对双方初始序号值得确认,也就无需再第四次握手了。
第一次握手:服务端确认“自己收、客户端发”报文功能正常。
第二次握手:客户端确认“自己发、自己收、服务端收、客户端发”报文功能正常,客户端认为连接已建立。
第三次握手:服务端确认“自己发、客户端收”报文功能正常,此时双方均建立连接,可以正常通信。
三 事务特性
事务就是一组原子性的操作,这些操作要么全部发生,要么全部不发生。事务把数据库从一种一致性状态转换成另一种一致性状态。
原子性。事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做
一致性。事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。
隔离性。一个事务的执行不能其它事务干扰。即一个事务内部的//操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
持续性。也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。
四 死锁是什么,怎么解决
死锁:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
死锁必须具备以下四个条件:
- 互斥条件:该资源任意一个时刻只由一个线程占用。
-
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
-
不剥夺条件:线程已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
-
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
避免死锁只要破坏产生死锁的四个条件中的其中一个就可以了
-
破坏互斥条件-----这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)
-
破坏请求与保持条件----一次性申请所有的资源。
-
破坏不剥夺条件----占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
-
破坏循环等待条件----靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。
-
锁排序法----- 指定获取锁的顺序,比如某个线程只有获得A锁和B锁,才能对某资源进行操作,在多线程条件下,如何避免死锁? 通过指定锁的获取顺序,比如规定,只有获得A锁的线程才有资格获取B锁,按顺序获取锁就可以避免死锁。这通常被认为是解决死锁很好的一种方法。
-
使用显式锁中的ReentrantLock.try(long,TimeUnit)来申请锁
五 并发和并行有什么区别
并发就是在一段时间内,多个任务都会被处理;但在某一时刻,只有一个任务在执行。单核处理器可以做到并发。比如有两个进程A
和B
,A
运行一个时间片之后,切换到B
,B
运行一个时间片之后又切换到A
。因为切换速度足够快,所以宏观上表现为在一段时间内能同时运行多个程序。
并行就是在同一时刻,有多个任务在执行。这个需要多核处理器才能完成,在微观上就能同时执行多条指令,不同的程序被放到不同的处理器上运行,这个是物理上的多个进程同时进行。
六 http和https区别
HTTP | HTTPS | |
---|---|---|
端口 | 80 | 443 |
安全性 | 无加密,安全性较差 | 有加密机制,安全性较高 |
资源消耗 | 较少 | 由于加密处理,资源消耗更多 |
是否需要证书 | 不需要 | 需要 |
协议 | 运行在TCP协议之上 | 运行在SSL协议之上,SSL运行在TCP协议之上 |
七 redis Zset底层数据结构
根据元素个数的不同,ZSet 采用了两种不同的结构实现。压缩列表(ziplist)和哈希表 + 跳表。当有序集合保存的元素个数小于等于 128 个,并且每个元素都不超过 64 字节时,会采用 ziplist 存储;否则使用哈希表 + 跳表存储。
八 redis持久化方法
为了能够重用Redis数据,或者防止系统故障,我们需要将Redis中的数据写入到磁盘空间中,即持久化。Redis提供了两种不同的持久化方法可以将数据存储在磁盘中,一种叫快照RDB
,另一种叫只追加文件AOF
。
RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘(Snapshot
),它恢复时是将快照文件直接读到内存里。
优势:适合大规模的数据恢复;对数据完整性和一致性要求不高
劣势:在一定间隔时间做一次备份,所以如果Redis意外down
掉的话,就会丢失最后一次快照后的所有修改。
AOF
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录),只许追加文件但不可以改写文件,Redis启动之初会读取该文件重新构建数据,换言之,Redis重启的话就根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。
AOF采用文件追加方式,文件会越来越大,为避免出现此种情况,新增了重写机制,当AOF文件的大小超过所设定的阈值时, Redis就会启动AOF文件的内容压缩,只保留可以恢复数据的最小指令集.。
优势
-
每修改同步:
appendfsync always
同步持久化,每次发生数据变更会被立即记录到磁盘,性能较差但数据完整性比较好 -
每秒同步:
appendfsync everysec
异步操作,每秒记录,如果一秒内宕机,有数据丢失 -
不同步:
appendfsync no
从不同步
劣势
-
相同数据集的数据而言
aof
文件要远大于rdb
文件,恢复速度慢于rdb
-
aof
运行效率要慢于rdb
,每秒同步策略效率较好,不同步效率和rdb
相同
九 输入URL,有哪些过程
浏览器根据请求的 URL 交给 DNS 域名解析,找到真实 IP ,向服务器发起请求;
服务器交给后台处理完成后返回数据,浏览器接收⽂件( HTML、JS、CSS 、图象等);
浏览器对加载到的资源( HTML、JS、CSS 等)进⾏语法解析,建立相应的内部数据结构 (如 HTML 的 DOM);
载⼊解析到的资源⽂件,渲染页面,完成。
十 算法
1.二叉树前中后序遍历
typedef struct TreeNode
{
int data;
TreeNode * left;
TreeNode * right;
TreeNode * parent;
}TreeNode;
void pre_order(TreeNode * Node)//前序遍历递归算法
{
if(Node == NULL)
return;
printf("%d ", Node->data);//显示节点数据,可以更改为其他操作。在前面
pre_order(Node->left);
pre_order(Node->right);
}
void middle_order(TreeNode *Node)//中序遍历递归算法
{
if(Node == NULL)
return;
middle_order(Node->left);
printf("%d ", Node->data);//在中间
middle_order(Node->right);
}
void post_order(TreeNode *Node)//后序遍历递归算法
{
if(Node == NULL)
return;
post_order(Node->left);
post_order(Node->right);
printf("%d ", Node->data);//在最后
}
2.区间合并
class Solution {
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> {
if (a[0] != b[0]) {
return a[0] - b[0];
} else {
return b[1] - a[1];
}
});
List<int[]> mergedList = new ArrayList<int[]>();
mergedList.add(intervals[0]);
int length = intervals.length;
for (int i = 1; i < length; i++) {
int[] curr = intervals[i];
int[] prev = mergedList.get(mergedList.size() - 1);
if (curr[0] == prev[0]) {
continue;
}
if (curr[0] <= prev[1]) {
prev[1] = Math.max(prev[1], curr[1]);
} else {
mergedList.add(curr);
}
}
return mergedList.toArray(new int[mergedList.size()][]);
}
}
3.接雨水
public int trap(int[] height) {
int sum = 0;
int[] max_left = new int[height.length];
int[] max_right = new int[height.length];
for (int i = 1; i < height.length - 1; i++) {
max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
}
for (int i = height.length - 2; i >= 0; i--) {
max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
}
for (int i = 1; i < height.length - 1; i++) {
int min = Math.min(max_left[i], max_right[i]);
if (min > height[i]) {
sum = sum + (min - height[i]);
}
}
return sum;
}