网络协议

一、 网络协议

1.1 网络模型

1.1.1 OSI七层模型

       开放系统互联参考模型(Open System Interconnect)是国际标准化组织(ISO)制订的一个用于计算机或通信系统间互联的标准体系。采用七层结构,自下而上依次为:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

1.1.2 TCP/IP模型

       是一组用于实现网络互连的通信协议。Internet网络体系结构以TCP/IP为核心。采用四层结构,自下而上依次为:链路层、网络层、传输层、应用层。

       两个模型之间的对应关系:
网络协议_第1张图片

       TCP/IP协议族
        TCP/IP协议簇是Internet的基础,也是当今最流行的组网形式。TCP/IP是一组协议的代名词,包括许多别的协议,组成了TCP/IP协议簇。
网络协议_第2张图片

1.2 TCP

       TCP是为了在不可靠的互联网络上提供一种面向连接的、可靠的、基于字节流的传输层通信协议。

1.2.1 TCP的三次握手(建立连接)

       建立一个TCP连接时需要客户端和服务端总共发送三次数据包以确认连接的建立。

       三次握手过程:
       1) 客户端发送SYN(SYN=x)报文给服务端,进入SYN_SEND状态。
       2) 服务端收到SYN报文,回应一个SYN(SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
       3) 客户端收到服务端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态,完成三次握手。
网络协议_第3张图片

       
       TCP三次握手的漏洞
       如果你一个客户端向服务端发送了SYN报文后突然死机或掉线,那么服务器在发送SYN+ACK报文后是无法收到客户端ACK报文的,这种情况下服务端一般会不停的重试,并等待一段时间(大约为30秒-2分钟)后丢弃未完成的链接。一个用户出现异常不是什么大问题,但如果一个攻击者发送大量伪造原IP地址的攻击报文到服务器,服务器将为了维护一个非常大的半连接队列而消耗更多的CPU和内存资源。服务器忙于处理伪造的TCP连接请求而无暇理睬客户的正常需求。这种情况称为服务器受到了SYN Flood攻击(SYN洪水攻击)。

解决方案:

  1. 缩短SYN无效时间
    过小的无效时间可能会影响到正常客户端连接,不建议使用。
  2. 延迟TCB分配
    一般第一次握手后,服务器会为该请求分配TCB(连接控制资源),通常需要200多字节。如果等到连接建立后再分配,可有效的减轻服务器资源的消耗。
  3. 防火墙
    防火墙在确认了连接的有效性后,才向内部的服务器(Listener)发起 SYN请求。

1.2.2 TCP四次挥手(连接终止)

       建立一个连接需要三次握手,而终止一个连接要经过四次挥手。这是由TCP的半关(half-close)造成的。

       具体过程如下:
       1) 客户端进程向服务端发送标志位是FIN的报文段,设置序列号为seq,此时,客户端进入FIN_WAIT_1状态,并且停止发送数据。
       2) 服务端收到客户端发送的FIN报文段,向客户端返回一个标志位为ACK(ack=seq+1)的报文段,服务器进入CLOSE_WAIT(关闭等待)状态。客户端收到服务器确认请求后,进入FIN_WAIT_2状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后数据)。
       3) 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文(FIN=1,ACK=seq+1),服务器进入LAST_ACK状态,等待客户端确认。
       4) 客户端收到服务端连接释放的报文后,发出确认报文,客户端进入TIME_WAIT状态。此时TCP连接还没释放,必须经过2MSL(最长报文寿命)的时间后,撤销TCB,进入CLOSED状态。服务端只要收到确认,立即进入CLOSED状态。
网络协议_第4张图片

 
       TCP的可靠性
       在TCP中,当发送端的数据达到接收主机时,接收主机会发回确认应答(ACK)。如果发送端收到确认应答,说明数据已成功发送到接收主机。反之,如果一段时间内没有收到确认应答,发送端就认为数据丢失,进行重发。因此即使产生丢包,仍然能够保证数据到达接收主机,实现可靠传输。
未收到确认应答并不意味着数据丢失,也有可能接收方已经收到数据,但是确认应答数据在途中丢失,这种情况发送端会误以为接收方没有收到而重发数据。
       对于接收方来说,反复收到相同的数据是不可取的。为了对上层应用提供可靠传输,接收主机必须放弃重复的数据包。因此引入了序列号。
序列号是按照顺序给发送数据的每一个字节(8 位字节)都标上号码的编号。接收端查询接收数据 TCP 首部中的序列号和数据的长度,将自己下一步应该接收的序列号作为确认应答返送回去。通过序列号和确认应答号,TCP 能够识别是否已经接收数据,又能够判断是否需要接收,从而实现可靠传输。

       TCP中的滑动窗口
       滑动窗口是TCP流量控制的一种方法。
首先第一次发送数据的时候窗口大小是根据链路带宽的大小决定的,假设是3。接收方收到数据后会对数据进行确认(ACK),并告诉发送方下次希望收到的数据是多少。发送方收到确认后按照发送方希望的数据大小发送。

1.3 UDP

       UDP(用户数据协议,User Datagram Protocol)为应用程序提供了一种无需建立连接就可以发送数据包的方法。UDP是一个不可靠的协议。
       使用UDP的服务主要包括:视频音频等多媒体通信、限定于局域网等特定网络中的通信、广播通信等。

1.4 HTTP

       HTTP(超文本传输协议,Hyper Text Transfer Protocol)是用于万维网(www)客户端浏览器和服务器进行传输的协议。

       一次完整HTTP请求的7个过程
       1) 三次握手建立TCP连接。
       2) 客户端向服务器发送请求命令。
       3) 客户端发送请求头信息。
       4) 服务器应答。
       ;5) 服务器向客户端发送数据。
       ;6) 关闭TCP连接。

       HTTP协议报文结构
       请求报文结构:
网络协议_第5张图片
网络协议_第6张图片
       响应报文结构:

网络协议_第7张图片网络协议_第8张图片

二、 JAVA原生网络编程

2.1 LINUX网络IO模型

       阻塞IO
       应用程序调用IO函数后阻塞,等待数据准备好。当数据准备好时,将数据从内核拷贝到用户空间,IO函数返回。
网络协议_第9张图片

       非阻塞IO
       当应用程序调用IO函数后不会阻塞,如果没有数据会返回一个错误,应用程序反复调用IO函数,直到数据准备好。不断调用的过程会消耗大量的CPU资源,不推荐使用。
网络协议_第10张图片

       IO多路复用
       IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题,此外poll、epoll都是这种模型。在该种模式下,用户首先将需要进行IO操作的 socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起 read请求,读取数据并继续执行。从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理 多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处 理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
网络协议_第11张图片

       信号驱动IO
       套接口进行信号驱动IO,并安装一个信号处理函数,进程继续运行而不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号函数调用IO函数处理数据。
网络协议_第12张图片

       异步IO
       当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作。
       LINUX底层使用epoll实现,是伪异步。
网络协议_第13张图片

       5种IO模型比较:
网络协议_第14张图片

       select、poll、epoll区别

  1. 打开的最大连接数不同。
    select最小,底层使用数组实现。
    poll最大,没有限制。底层使用链表实现。
    epoll非常大,有限制。1G内存支持10万个连接。
  2. IO效率不同。
    FD(文件描述符)增加后带来的IO效率问题。
    因为每次调用时都会对连接进行遍历,所以随着 FD(文件描述符) 的增加select和poll性能呈线性下降。而epoll只关注活跃连接,所以在活跃连接较少的情况下,性能高于前两者。
  3. 消息(报文)传递方式不同
    select、poll:数据需要从内核空间传输到用户空间。
    epoll:通过内核和用户空间共享一块内存实现的。

2.2 BIO

       传统的BIO通信模型:服务端通常由一个独立Acceptor线程负责监听客户端连接,收到客户端请求后为每个客户端创建一个新的线程进行处理,处理完成后通过输出流返回应答给客户端。该模型最大的问题是当请求增多,线程数量增加,系统性能急剧下降。可以使用线程池进行改进。

       服务端代码:

public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(8888));

        while (true) {
            new Thread(new Task(serverSocket.accept())).start();
        }
    }

    static class Task implements Runnable {

        private Socket socket;

        Task(Socket socket) {
            this.socket = socket;
        }

        @SneakyThrows
        @Override
        public void run() {
            ObjectOutputStream outputStream = null;
            ObjectInputStream inputStream = null;


            try {
                inputStream = new ObjectInputStream(socket.getInputStream());
                String name = inputStream.readUTF();

                System.out.println("收到客户端发来的信息....." + name);

                outputStream = new ObjectOutputStream(socket.getOutputStream());
                outputStream.writeUTF("Hello," + name);
                outputStream.flush();

            } finally {
                if (inputStream != null) inputStream.close();
                if (outputStream != null) outputStream.close();
            }
        }
    }
}

       客户端代码:

public class Client {

    private final static String IP = "127.0.0.1";
    private final static int PORT = 8888;

    public static void main(String[] args) throws IOException {


        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(IP, PORT));

        ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());

        outputStream.writeUTF("hello");

        outputStream.flush();

        ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
        String context = inputStream.readUTF();
        System.out.println("收到了服务器回复的消息..." + context);

        outputStream.close();
        inputStream.close();
        socket.close();
    }
}

2.3 NIO

2.3.1 NIO与BIO的区别

  1. NIO是面向缓冲区的,BIO是面向流的。
  2. NIO是非阻塞的,BIO是阻塞的。
  3. NIO使用Selectors(选择器)。

2.3.2 NIO主要由三个核心部分组成

  1. Selector(选择器) 应用程序将向 Selector 对象注册需要它关注的 Channel,以及具体的某一个Channel会对哪些 IO 事件感兴趣。Selector 中也会维护一个“已经注册的 Channel” 的容器。
  2. Channel(通道)
    应用程序和操作系统交互事件、传递内容的渠道。应用程序可以通过通道读取数据,也可以通过通道向操作系统写数据。
    ServerSocketChannel ScoketChannel
  3. Buffer(缓冲区)

你可能感兴趣的:(java进阶知识总结,网络协议,网络)