zoukankan      html  css  js  c++  java
  • 面试之网络基础

    零、基础知识

    OSI七层理论体系结构

    1. 物理层:解决两台主机的通信问题——A往B发送比特流(0101),B能接收到这些比特流。定义了物理设备的标准如网线的类型,光纤的接口类型以及传输介质的传输速率等。

    2. 数据链路层:由于物理层上的传输的比特流可能会出现错传、误传等,所以数据链路层定义了如何格式化数据即将比特流封装成,提供了错误检测。

    3. 网络层:随着节点的增加,点对点通信是需要经过多个节点的,如何找到目标节点,如何找到最优路径变成为了首要需求。所以出现了网络层,主要目的是将网络地址翻译成对应的物理地址,分组传输、路由选择,本层的传输单位是数据报(分组),本层需要注意的TCP/IP协议中的TCP协议。

    4. 传输层:随着网络需要的进一步扩大,通信过程中需要传输大量的数据,网络可能会发生中断,为了保证传输大量文件时的准确性,需要对发送的数据进行切分,切分成一个个的segment进行发送,考虑如何在接受方拼接切分的segment组成完整的数据,以及发现丢失segment时该如何处理,需要注意的协议TCP、UDP。

    5. 会话层:不同机器上的用户之间建立以及管理会话。用于保证应用程序自动收发包和寻址。

    6. 表示层:信息的语义语法,加密解密,转换翻译,压缩解压缩。

    7. 应用层:规定双方必须使用固定长度的消息头,且消息头必须记录消息长度等信息。需要注意的是TCP/IP协议中的HTTP协议。

    TCP/IP四层模型

    是OSI的一种实现,包括应用层、运输层、网际层和网络接口层。

    一、TCP的三次握手

    传输控制协议TCP:

    • 是面向连接的、可靠的、基于字节流的传输层通信协议。
    • 将应用层的数据流分割成报文段并发送给目标节点的TCP层。
    • 数据包都有序号,对方收到则发送ACK确认,未收到则重传。
    • 使用校验和来检验数据在传输过程中是否有误。

    序号字段的值则指的是本报文段所发送的数据的第一个字节的序号。

    确认号字段——占 4 字节,是期望收到对方的下一个报文段的数据的第一个字节的序号。

    确认 ACK —— 只有当 ACK = 1 时确认号字段才有效。

    同步 SYN —— 同步 SYN = 1 表示这是一个连接请求或连接接受报文。

    终止 FIN (FINish) —— 用来释放一个连接。FIN = 1 表明此报文段的发送端的数据已发送完毕,并要求释放运输连接。

    TCP的三次握手回答

    1. 第一次握手:建立连接时,客户端发送请求SYN包,SYN=1,seq=x,客户端进入SYN_SENT状态,等待服务器的确认。

    2. 第二次握手:服务器收到SYN报文段,需要对这个SYN报文段进行确认,设置ack=x+1,同时自己还要发送SYN请求信息给客户端,所以SYN=1,seq=y。服务器将上述信息放到SYN+ACK报文段中一并发给客户端,此时服务器进入SYN_RECV状态。

    3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(其中ack=y+1),此包发送完毕后,客户端和服务器进入ESTABLISHED状态,完成三次握手。

    二、TCP的四次挥手

    TCP的四次挥手回答:TCP采用四次挥手来释放连接。

    1. 第一次挥手:Client发送一个FIN包,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态;
    2. 第二次挥手:Server收到FIN包后,发送一个ACK包给Client,其中确认序号ack为收到序号+1,Server进入CLOSE_WAIT状态;
    3. 第三次挥手:Server发送一个FIN包用来关闭Server到Client的数据传送,Server进入LAST_ACK状态;
    4. 第四次挥手:Client收到FIN包后,Client进入TIME_WAIT状态,接着发送一个ACK包给Server,确认序号ack为收到序号+1,Server进入CLOSED状态,完成四次挥手。

    为什么有TIME_WAIT状态?

    1. 确保有足够的时间让对方收到ACK包
    2. 避免新旧连接混淆

    为什么需要四次挥手才能断开连接?

    因为全双工,发送方和接收方都需要FIN报文和ACK报文。

    三、TCP和UDP的区别

    UDP报文示意图:

    UDP的特点:

    • 面向非连接的
    • 不维护连接状态,支持同时向多个客户端传输相同的消息
    • 数据包报头只有8个字节(源端口、目的端口、长度、校验和),额外开销较小
    • 吞吐量只受限于数据生成速率、传输速率以及机器性能
    • 尽最大努力交付,不保证可靠交付,不需要维持复杂的连接状态表
    • 面向报文,不对应用程序提交的报文进行拆分或者合并

    TCP和UDP的区别回答:

    1. TCP是面向连接的,UDP是无连接的
    2. TCP比UDP是更可靠的(握手和确认重传机制)
    3. TCP是有序的,UDP是无序的。
    4. TCP速度相比UDP慢(需要建立连接)
    5. TCP相比UDP的开销更大(TCP首部20个字节,UDP首部8个字节)

    四、HTTP与HTTPS

    HTTP是在TCP/IP四层模型中的协议,超文本传输协议HTTP主要特点如下:

    • 支持客户端/服务器模式
    • 简单快速
    • 灵活
    • 无连接
    • 无状态

    面:在浏览器地址栏中输入URL,按下回车之后经历的流程

    回答:

    1. DNS解析:首先浏览器会根据URL逐层查询DNS服务器缓存,解析URL中域名所对应的IP地址,DNS缓存从近到远依次是:浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,当找到IP后,直接返回无需查询下一层缓存。
    2. TCP连接:根据IP和端口(默认80)和服务器建立TCP连接,通过TCP的三次握手。
    3. 发送HTTP请求
    4. 服务器处理请求并返回HTTP报文
    5. 浏览器解析响应报文并渲染页面
    6. 连接结束,浏览器释放TCP连接,通过TCP的四次挥手。

    面:说说常见的HTTP状态码。

    答:HTTP响应状态码有5种可能的取值:

    • 1xx:指示信息--表示请求已接收,继续处理
    • 2xx:成功--表示请求已被成功接收、理解、接受
    • 3xx:重定向--要完成请求必须进行更进一步的操作
    • 4xx:客户端错误--请求有语法错误或请求无法实现
    • 5xx:服务器端错误--服务器未能实现合法的请求。

    面:GET请求和POST请求的区别

    1. Http报文层面:GET请求将请求信息放在URL中,POST将请求信息放在报文体中
    2. 数据库层面:GET符合幂等性(对数据库的一次操作或多次操作获得的结果是一致的)和安全性(对数据库的操作没有改变数据库的数据),POST不符合。
    3. 其他层面:GET请求可以被缓存、被存储,而POST不行

    Cookie和Session的区别

    Cookie简介:

    • 是由服务器发给客户端的特殊信息,以文本的形式存放在客户端
    • 客户端再次请求时,会把Cookie回发
    • 服务器接收到后,会解析Cookie生成与客户端相对应的内容

    Cookie的设置以及发送过程如下:

    Session简介:

    • 服务器端的机制,在服务器上保存的信息
    • 解析客户端请求并操作session id,按需保存状态信息

    Session的实现方式:

    1. 使用Cookie实现

    2. 使用URL回写来实现(返回给浏览器的所有链接中都携带JSESSIONID参数)

    面:Cookie和Session的区别

    1. Cookie数据存放在客户的浏览器上,Session数据放在服务器上
    2. Session相对于Cookie更安全
    3. 若考虑减轻服务器的负担,应当使用Cookie

    HTTPS

    HTTPS相对于HTTP就是多了一层SSL(Security Sockets Layer,安全套接层)。SSL定义如下:

    • 为网络通信提供安全及数据完整性的一种安全协议
    • 是操作系统对外的API,SSL3.0后更名为TLS
    • 采用身份验证和数据加密保证网络通信的安全和数据的完整性

    加密的方式

    • 对称加密:加密和解密都使用同一密钥
    • 非对称加密:加密使用的密钥和解密使用的密钥是不同的
    • 哈希算法:将任意长度的信息转为固定长度的值算法不可逆
    • 数字签名:证明某个消息或者文件是某人发出/认同的

    HTTPS数据传输流程

    • 浏览器将支持的加密算法信息发送给服务器
    • 服务器选择一套浏览器支持的加密算法,以证书的形式回发浏览器
    • 浏览器验证证书合法性,并结合证书公钥加密信息发送给服务器
    • 服务器使用私钥解密信息,验证哈希,加密响应消息回发浏览器
    • 浏览器解密响应消息,并对消息进行验证,之后进行加密交互数据

    面:HTTP和HTTPS的区别

    1. HTTPS需要到CA申请证书,HTTP不需要
    2. HTTPS密文传输,HTTP明文传输
    3. 连接方式不同,HTTPS默认使用443端口,HTTP使用80端口
    4. HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全

    五、Socket

    我们知道如果两个进程如果要进行通信的话,那么首先应该能够唯一地标识这2个进程,在本地进程中我们可以使用PID唯一标识进程,那么在网络中呢?在网络中,各个主机的PID可能会重复,因此光靠PID就不能唯一标识进程了。前面我们知道TCP/IP协议中的IP能够唯一标识主机,而TCP/IP中的端口号又能唯一标识相应主机的唯一进程,因此可以通过IP+协议+端口号来唯一标识网络中的一个进程。Socket就是通过这种方式来进行网络中进程的通信的。

    Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口。

    Socket通信流程图如下:

    Socket相关的面试题:编写一个网络应用程序,有客户端和服务端,客户端向服务器发送一个字符串,服务器收到该字符串后将其打印到命令行上,然后向客户端返回给字符串的长度,最后客户端输出服务端返回的该字符串的长度,分别用TCP和UDP两种方式实现。

    • TCP方式
    package com.yunche.socket;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketAddress;
    import java.util.Arrays;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @ClassName: TCPServer
     * @Description: TCP服务端
     * @author: yunche
     * @date: 2019/03/11
     */
    public class TCPServer {
    
        public static void main(String[] args) throws IOException {
            //创建socket,并将socket绑定到65000端口
            ServerSocket serverSocket = new ServerSocket(65000);
            ExecutorService executor = Executors.newCachedThreadPool();
            while (true) {
                //监听65000端口,直到客户端返回连接信息才返回
                Socket socket = serverSocket.accept();
                //获取客户端的的请求信息,执行相关的业务逻辑
                executor.execute(()->{
                    try {
                        service(socket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    
        private static void service(Socket socket) throws IOException {
            //获取socket的输入流
            InputStream is = socket.getInputStream();
            //获取socket的输出流
            OutputStream os = socket.getOutputStream();
            byte[] bytes = new byte[1024];
            //读取的字节数
            int ch = is.read(bytes);
            String content = new String(bytes, 0, ch);
            //输出收到的字符串
            System.out.println(content);
            //往输出流中写服务端收到的客户端的字符串的长度
            os.write(String.valueOf(content.length()).getBytes());
    
            //关闭流以及socket
            is.close();
            os.close();
            socket.close();
        }
    }
    
    package com.yunche.socket;
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    
    /**
     * @ClassName: TCPClient
     * @Description: TCP客户端
     * @author: yunche
     * @date: 2019/03/11
     */
    public class TCPClient {
        public static void main(String[] args) throws IOException {
            //创建socket,并指定连接的是本机的端口为65000的服务器socket
            Socket socket = new Socket("localhost", 65000);
            //获取输出流
            OutputStream os = socket.getOutputStream();
            //获取输入流
            InputStream is = socket.getInputStream();
            //将要传递给server的字符串参数转换成byte数组,并将数组写入到输出流中
            os.write("hello world".getBytes());
            //用来读取输入的内容,即从服务器返回的字符串长度
            byte[] bytes = new byte[1024];
            int ch = is.read(bytes);
            String len = new String(bytes, 0, ch);
            System.out.println(len);
    
            //关闭相应的流以及socket
            is.close();
            os.close();
            socket.close();
        }
    }
    
    • UDP方式
    package com.yunche.socket;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.Socket;
    import java.net.SocketException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @ClassName: UDPServer
     * @Description:
     * @author: yunche
     * @date: 2019/03/11
     */
    public class UDPServer {
        public static void main(String[] args) throws IOException {
            //创建一个底层是UDP的socket
            DatagramSocket socket = new DatagramSocket(65001);
            //存储从客户端接受到的内容
            byte[] bytes = new byte[1024];
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
            //接受客户端发送过来的内容,并将内容封装进DatagramPacket对象中
            socket.receive(packet);
    
            //从DatagramPacket对象中获取到真正存存储的数据
            byte[] data = packet.getData();
            //将数据从二进制转换成字符串
            String content = new String(data, 0, packet.getLength());
            System.out.println(content);
    
            //将要发送给客户端的数据转为二进制
            byte[] len = String.valueOf(content.length()).getBytes();
            //服务端给客户端发送数据报
            //从DatagramPacket对象中获取到数据的来源地址与端口号
            DatagramPacket packetToClient = new DatagramPacket(len, len.length, packet.getAddress(),packet.getPort());
            socket.send(packetToClient);
            }
    }
    
    package com.yunche.socket;
    
    import java.io.IOException;
    import java.net.*;
    
    /**
     * @ClassName: UDPClient
     * @Description: UDP客户端
     * @author: yunche
     * @date: 2019/03/11
     */
    public class UDPClient {
        public static void main(String[] args) throws IOException {
            //客户端发送数据报给服务端
            DatagramSocket socket = new DatagramSocket();
            byte[] bytes = "Hello world".getBytes();
            //将IP地址封装成InetAddress对象
            InetAddress address = InetAddress.getByName("localhost");
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length, address,65001);
            //发送数据给服务端
            socket.send(packet);
    
            //客户端接受从服务端返回的数据报
            byte[] data = new byte[1024];
            DatagramPacket receivePacket = new DatagramPacket(data, data.length);
            socket.receive(receivePacket);
            String len = new String(data, 0, receivePacket.getLength());
            System.out.println(len);
        }
    }
    

    参考资料

    慕课网 剑指Java面试-Offer直通车

    OSI七层模型详解

  • 相关阅读:
    matlab画图-在同一图像中显示多个函数
    matlab简单作图2
    matlab简单作图
    c++ this指针概念
    c++ 静态成员
    C++ 类对象作为类成员
    (C++核心编程 )初始化列表
    (C++核心编程 )点和圆的关系
    (C++核心编程)设计立方体类
    (python基础 函数)
  • 原文地址:https://www.cnblogs.com/yunche/p/10510172.html
Copyright © 2011-2022 走看看