网络编程

一、计网OSI、TCP/IP参考模型

分层结构优点

  • 开放的标准化接口
  • 多厂商兼容
  • 易于理解学习和更新协议标准
  • 模块化过程,降低开发实现复杂度
  • 便于故障排除

OSI模型

OSI是Open System Interconnect的缩写,意为开放式系统互联。OSI七层参考模型的各个层次的划分遵循下列原则:

  1. 同一层中的各网络节点都有相同的层次结构,具有同样的功能。
  2. 同一节点内相邻层之间通过接口(可以是逻辑接口)进行通信。
  3. 七层结构中的每一层使用下一层提供的服务,并且向其上层提供服务。
  4. 不同节点的同等层按照协议实现对等层之间的通信。

OSI参考模型各个层次分层和基本功能如下:

  1. 物理层:在设备之间传输比特流,规定了电平、速度和电缆针脚。
  2. 数据链路层:将比特组合成字节,再将字节组合成帧,使用链路层地址(以太网使用MAC地址)来访问介质,并进行差错检测。
  3. 网络层:提供逻辑地址,供路由器确定路径。
  4. 传输层:提供面向连接或非面向连接的数据传递以及进行重传前的差错检测。
  5. 会话层:负责建立、管理和终止表示层实体之间的通信会话。该层的通信由不同设备中的应用程序之间的服务请求和响应组成。
  6. 表示层:提供各种用于应用层数据的编码和转换功能,确保一个系统的应用层发送的数据能被另一个系统的应用层识别。
  7. 应用层:OSI参考模型中最靠近用户的一层,为应用程序提供网络服务。
    在这里插入图片描述

TCP/IP模型

ISO/OSI参考模型是在其协议被开发之前设计出来的。这意味着ISO/OSI模型并不是基于某个特定的协议集而设计的,因而它更具有通用性。但另一方面,也意味着ISO/OS1模型在协议实现方面存在某些不足。而TCP/IP模型正好相反。先有协议,模型只是现有协议的描述,因而协议与模型非常吻合。问题在于TCP/IP模型不适合其他协议栈。因此,它在描述其他非TCP/IP网络时用处不大。

在这里插入图片描述

  • 链路层又叫数据链路层,网络接口层等
  • 每一层如果要对应具体的实现场所的话,应用层是通过应用程序实现的,传输层和网际层是通过操作系统实现的,而数据链路层则是通过网卡以及网络接口设备等硬件设备实现的

两种模型联系

OSI模型仅仅是一个概念,并没有提供实现,所以OSI模型仅仅是在制定标准时的概念性框架;OSI概念的真正实现者是 TCP/IP四层模型,所以TCP/IP四层模型才是事实上的标准模型
在这里插入图片描述

二、TCP、UDP、IP、HTTP协议

TCP协议

TCP协议作用

TCP协议位于协议栈的传输层。当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流,TCP则把数据流分割成适当长度的报文段,最大传输段大小(MSS)通常受该计算机连接的网络的数据链路层的最大传送单元(MTU)限制。之后TCP把数据包传给IP层,由它来通过网络将包传送给接收端实体的TCP层。

TCP为了保证报文传输的可靠,就给每个包一个序号,同时序号也保证了传送到接收端实体的包的按序接收。然后接收端实体对已成功收到的字节发回一个相应的确认(ACK);如果发送端实体在合理的往返时延(RTT)内未收到确认,那么对应的数据(假设丢失了)将会被重传。

在数据正确性与合法性上,TCP用一个校验和函数来检验数据是否有错误,在发送和接收时都要计算校验和;同时可以使用md5认证对数据进行加密。

在保证可靠性上,采用超时重传和捎带确认机制。

在流量控制上,采用滑动窗口协议,协议中规定,对于窗口内未经确认的分组需要重传。

在拥塞控制上,采用广受好评的TCP拥塞控制算法(也称AIMD算法)。 该算法主要包括三个主要部分:

  • 加性增、乘性减;
  • 慢启动;
  • 对超时事件做出反应。
TCP的报头

在这里插入图片描述

源端口:发送方的端口号

目的端口:接受方的端口号

序号:发送方的序号,用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。

确认序号:接受方得到序号之后回复的确认序号

数据偏移 :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。

TCP 首部长度:4 bits,以32-bit字为单位。TCP首部长短,也是TCP报文数据部分的偏移量。范围5~15,即20 bytes ~ 60 bytes。可选项部分最多允许40 bytes。

标志位,标志位主要用户标志该报文当前的状态。

URG:指示报文中有紧急数据,应尽快传送(相当于高优先级的数据)。
ACK:确认序号(AN)有效。 当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
PSH:接到后尽快交付给接收的应用进程。
RST:TCP连接中出现严重差错(如主机崩溃),必须释放连接,再重新建立连接。
SYN:在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
FIN:发送端已完成数据传输,请求释放连接。当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。

窗口 :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。

TCP协议的连接时候的三次握手

TCP是一个面向连接的协议,在每一次传输数据前,客户端和服务端需要进行连接,这个链接就是着名的三次握手。

第一次:客户端向服务端发送一个 SYN(SEQ=x 客户端序号)报文给服务器端,进入SYN_SEND状态。

第二次:服务器端收到SYN报文,回应一个SYN (SEQ=y 服务端序号)ACK(ACK=x+1 确认号=客户端序号+1)报文,进入SYN_RECV状态。

第三次:客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。

图解:

三次握手的原因

第三次握手是为了防止失效的连接请求到达服务器,让服务器错误打开连接。

客户端发送的连接请求如果在网络中滞留,那么就会隔很长一段时间才能收到服务器端发回的连接确认。客户端等待一个超时重传时间之后,就会重新请求连接。但是这个滞留的连接请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三次握手,客户端会忽略服务器之后发送的对滞留连接请求的连接确认,不进行第三次握手,因此就不会再次打开连接。

为什么要进行三次握手,而不是两次呢?

比如在第一次握手之后,服务器进入准备状态,然后发送消息给客户端,客户端也进入准备状态,这就完成了双方的确认了。

回答:两次握手时,服务器提前进入准备状态之后,如果中途遇到网络中断,消息并没有传回给客户端,客户端将永远接不到服务器的给入状态,那么服务端将资源浪费在一个不存在的连接之上了。

TCP协议的断开连接时候的四次挥手

既然TCP面向连接,那么肯定也有断开连接的操作。一个TCP完整的断开需要进行四次挥手。

第一次:客户端向服务端发送 FIN + ACK 报文,同时携带序号为 X。 客户端进入 FIN-WAIT1

第二次:服务器端回复 ACK 报文。附带序号Z和确认序号X+1,表示服务器已经接受到了客服端的报文。但是由于服务器可能还在处理事务,因此,报文并不会携带FIN标志。状态:CLOSE WAIT

第三次:在一段时间之后,服务器已经处理完毕,发送带有 FIN和ACK的报文,序号为Y,确认序号为 X + 1 。 状态: ACK-LAST

第四次:客户端发送ACK报文,序号为 X+1,确认号Y+1 。 客户端进入: TIME_WAIT。服务端进图CLOSE(初始状态)
在这里插入图片描述

四次挥手的原因

客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。

客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由:
  • 确保最后一个确认报文能够到达。如果 B 没收到 A 发送来的确认报文,那么就会重新发送连接释放请求报文,A 等待一段时间就是为了处理这种情况的发生。
  • 等待一段时间是为了让本连接持续时间内所产生的所有报文都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文。
为什么建立连接是三次握手,而关闭连接却是四次挥手呢?

回答:这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。

代码

服务端


 
 
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
 
/**
 * 实现TCP协议的服务器端
 * 便是服务器程序的类: java.net.ServerSocket
 * 构造方法:
 *    ServerSocket(int port) 传递端口号
 *
 * 服务端无法自己创建Socket类,需要获取到客户端的Socket套接字对象
 * (服务端接收到很多客户端链接申请,无法知道是那个客户端的申请,所以需要服务端获取到客户端的Socket套接字对象.来确定那个客户端的申请)
 * 方法:
 *   Socket accept()
 */
public class TCPServerSocket {
    public static void main(String []args)throws IOException {
        //创建服务器程序类.
        ServerSocket serversocket = new ServerSocket(7777);
        //使用服务器程序类Socket accept()方法获取客户端套接字Socket类对象
        Socket socket = serversocket.accept();
        //通过套接字获取到,字节输入流对象
        InputStream ips = socket.getInputStream();
        //创建字节数组,存储输入流读取客户端的字节数据
        byte[] bytes = new byte[1024];
        int len = ips.read(bytes);
        System.out.println(new String(bytes,0,len));
        //向客户端传递数据
        OutputStream pts = socket.getOutputStream();
        pts.write("服务器".getBytes());
        //关闭资源
        pts.close();
        ips.close();
        socket.close();
    }
}

客户端


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
 
/**
 * 此类实现TCP协议的客户端,链接到服务器
 * 和服务器实现数据交换
 * 实现TCP客户端程序类 java.net.Socket
 *
 * 构造方法:
 *   Socket(String host,int port) 传递服务器IP和端口号
 *   注意构造器一旦运行就会和服务器进行链接,链接失败,抛出异常
 *
 * 由于是在互联网传输数据使用的字节输出流和字节输入流必须使用Socket类的getxxxxx()方法返回的字节输出流和输入流对象
 * Socket 被称呼为套接字
 *
 * 输出流和输入方法:
 *    OutputStream getOutputStream() 返回套接字的输出流
 *    作用:将数据输出,输出到服务器 (服务器运行Socket方法获取到套接字输出流则输出到Socket对象客户端)
 *
 *    InputStream getInputStream() 返回套接字输入流
 *    作用:从服务端中读取数据 (服务器运行Socket方法获取到套接字输入流则读取客户端数据)
 */
public class TCPSocket {
    public static void main(String []args)throws IOException{
        //创建Socket类对象,链接服务器
        Socket socket = new Socket("127.0.0.1",7777);
        //通过客户端套接字对象Socket方法,获取字节输出流,将数据写入服务器
        OutputStream ops = socket.getOutputStream();
        //字节输出流,传递只能是字节,传递字节数组
        ops.write("客户端".getBytes());
        //读取服务器传递的数据
        InputStream ips = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = ips.read(bytes);
        System.out.println(new String(bytes,0,len));
        //关闭资源
        ips.close();
        ops.close();
        socket.close();
    }
}

UDP协议

UDP协议全称是用户数据包协议,在网络中它与TCP协议一样用于处理数据包,两者同处于协议栈的传输层,和TCP不同的是,UDP是一种无连接的协议。

UDP报头

因为UDP是无连接的,所以相对来说,UDP的报头比TCP要简单多了。
在这里插入图片描述

UDP 特点

  1. UDP是一个非连接的协议,传输数据之前源端和终端不建立连接,当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制;在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。
  2. 由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等,因此一台服务机可同时向多个客户机传输相同的消息。
  3. UDP信息包的标题很短,只有8个字节,相对于TCP的20个字节信息包的额外开销很小。
  4. 吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。
  5. UDP使用尽最大努力交付,即不保证可靠交付,因此主机不需要维持复杂的链接状态表(这里面有许多参数)。
  6. UDP是面向报文的。发送方的UDP对应用程序交下来的报文,在添加首部后就向下交付给IP层。既不拆分,也不合并,而是保留这些报文的边界,因此,应用程序需要选择合适的报文大小。

我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常,其实“ping”命令的原理就是向对方主机发送UDP数据包,然后对方主机确认收到数据包,如果数据包是否到达的消息及时反馈回来,那么网络就是通的。

DatagramPacket

UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像是货运公司在两个码头间发送货物一样。在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。

在这里插入图片描述

DatagramSocket

在这里插入图片描述

代码

发送端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
 
/**
 * 实现UDP协议的发送端
 *  UDP发送端和接收端必须有DatagramPacket类对象和DatagramSocket类对象
 *  发送端使用DatagramPacket类封装数据包使用DatagramSocket类发送数据包
 *  接收端需要DatagramSocket类接收数据包使用DatagramPacket类拆封数据包
 *
 *      实现封装数据类 java.net.DatagramPacket  将数据封装为数据包
 *      实现数据包传输类 java.net.DatagramSocket 将数据包发出去
 *
 * 实现步骤:
 *  1.创建DatagramPacket 对象封装数据,接收的地址和端口
 *  2.创建DatagramSocket 对象
 *  3.调用DatagramSocket 类send(DatagramPacket dp)方法传递数据包,发送数据包
 *  4.关闭资源close();
 *
 *  DatagramPacket构造方法:
 *     DatagramPacket(byte[] buf,int length,InetAddress address,int port)
 *     buf: 字节数组
 *     length: 需要传递的数组元素个数
 *     address: 需要发送的主机地址
 *     port: 端口号
 *
 *  DatagramSocket构造方法:
 *     DatagramSocket()
 *     方法:send(DatagramPacket dp)
 */
public class UDPSend {
    public static void main(String []args)throws IOException{
        //创建数据包对象,封装要发送的数据,接收端ip,端口
        byte[] data = "UDP发送".getBytes();
        InetAddress address = InetAddress.getByName("127.0.0.1");//ip: 127.0.0.1 是指本机
        //封装数据包
        DatagramPacket dp = new DatagramPacket(data,data.length,address,6000);
        //创建发送数据包
        DatagramSocket ds = new DatagramSocket();
        //调用发送数据包方法传递数据包对象
        ds.send(dp);
        //关闭资源
        ds.close();
    }
}

接收端

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
 
/**
 * 实现UDP接收端
 *      实现拆封数据包 java.net.DatagramPacket 调用方法对数据包拆包
 *      实现接收数据包 java.net.DatagramSocket 接收数据包端口调用方法封装数据包到DatagramPacket类对象
 *
 * 实现步骤:
 *   1.创建DatagramSocket对象,绑定端口号  必须要和发送端(发送数据包到的端口号)一致
 *   2.创建字节数组,接收发来的数据
 *   3.创建DatagramPacket类对象
 *   4.调用DatagramSocket对象方法  receive(DatagramPacket dp) 接收数据,将数据封装到数据包对象中
 *   5.拆包
 *      发送端ip地址
 *        数据包对象DatagramPacket类方法getAddress()返回封装ip地址对象
 *      接收到的字节个数
 *        数据包对象DatagramPacket类方法getLength() 返回数据包中的字节数组长度
 *      发送方的端口
 *        数据包对象DatagramPacket类方法getport() 返回发送或接收端的端口号
 *   6.关闭资源
 */
public class UDPReceive {
    public static void main(String []args)throws IOException{
        //在构造方法中指定端口号
        DatagramSocket ds = new DatagramSocket(6000);
        //由于是接收,所以只指定数组长度 一般指定数组长度为 64kb(也就是1024*64)
        byte[] bytes = new byte[1024];//但是由于我们发送的信息少所以数组长度指定为1kb
        //第一个参数传递字节数组用于接收拆封数据包后的数据 第二个参数传递需要接收的数组长度
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        //调用接收端方法接收数据,传递封装数据包对象
        ds.receive(dp);
 
        //调用getAddress() 返回主机地址对象(数据包中封装了发送端的主机ip和名字)
        InetAddress ip = dp.getAddress();
        System.out.println(ip.getHostAddress()+"  "+ip.getHostName());
 
        //获取接收到的字节个数
        int length = dp.getLength();
        System.out.println(length);
        //获取发送方的端口号
        int port = dp.getPort();
        System.out.println(port);
 
        //使用字符串格式打印字节数组
        System.out.println(new String(bytes,0,length));//由于我们接收端的数组长度过长造成资源浪费所以使用字符串的构造方法指定数组中间段的数据
        //关闭资源
        ds.close();
    }
}

TCP和UDP的区别

TCP的优点:

可靠,稳定 TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。

TCP的缺点:

慢,效率低,占用系统资源高,易被攻击 TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。 而且,因为TCP有确认机制、三次握手机制,这些也导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

UDP的优点:

快,比TCP稍安全 UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击……

UDP的缺点:

不可靠,不稳定 因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。 基于上面的优缺点,那么: 什么时候应该使用TCP: 当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。 在日常生活中,常见使用TCP协议的应用如下: 浏览器,用的HTTP FlashFXP,用的FTP Outlook,用的POP、SMTP Putty,用的Telnet、SSH QQ文件传输 …………

什么时候应该使用UDP: 当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。 比如,日常生活中,常见使用UDP协议的应用如下: QQ语音 QQ视频 TFTP ……
有些应用场景对可靠性要求不高会用到UPD,比如长视频,要求速率
在这里插入图片描述

IP协议

IP协议处于TCP/IP协议簇的网络互联层。它提供不可靠、无连接的服务,也即依赖其他层的协议进行差错控制。在局域网环境,IP协议往往被封装在以太网帧中传送。而所有的TCP、UDP、ICMP、IGMP数据都被封装在IP数据包包中传送。

在IP协议中,有两个重要的内容需要了解下。一是IP地址的概念,二是IP协议的报头。

IP地址的概念

其实对于IP地址我们日常接触还是挺多的。它给每一个接入互联网的计算器一个地址,从而使得其他的计算机能够访问到它。与此同时,当计算机有了地址之后,才能遵循IP协议,和其他的计算机进行数据的传递。

目前有两种IP版本,分别是IPV4和IPV6。IPV4占用8个字节32bit,而IPV6则是32个字节128bit。IPV6的可用的数量极其庞大,大到全球每一粒沙子都可以分配一个IPV6地址。

IP协议是TCP/IP协议族中最核心的协议。所有的TCP、UDP、ICMP、IGMP数据都以IP数据报的格式传输。

IP协议是不可靠、无连接的:

  • 不可靠表示IP协议不能保证IP数据报能成功的到达目的地。IP仅提供传输服务,任何可靠性的要求都必须由上层来提供(如TCP)。如果传输过程发生错误,IP协议简单的丢弃该数据报,然后发送ICMP消息给发送端。

  • 无连接表示IP协议不维护任何关于后续数据报的状态信息,每个数据报都是相互独立的。这也说明,IP数据报可能不是按照发送顺序被接收到的,很有可能后发送的数据被先收到。

HTTP协议

HTTP是一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。HTTP协议的主要特点可概括如下:

  • 支持客户/服务器模式。
  • 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
  • 灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type加以标记。
  • 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

HTTP相应报文:

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

三、RESTFUL设计架构

如果一个架构符合REST原则,就称它为RESTful架构。

REST的名称"表现层状态转化"中,省略了主语。“表现层"其实指的是"资源”(Resources)的"表现层"。

所谓"资源",就是网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。你可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的URI。要获取这个资源,访问它的URI就可以,因此URI就成了每一个资源的地址或独一无二的识别符。

所谓"上网",就是与互联网上一系列的"资源"互动,调用它的URI。

访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。

互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

综上总结一下什么是RESTful架构:

  1. 每一个URI代表一种资源;
  2. 客户端和服务器之间,传递这种资源的某种表现层;
  3. 客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

四、IO阻塞和非阻塞的主要问题

阻塞IO模型

在这里插入图片描述
进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程。操作成功则进程获取到数据。

典型应用:阻塞socket、Java BIO;

特点:

  1. 进程阻塞挂起不消耗CPU资源,及时响应每个操作;
  2. 实现难度低、开发应用较容易;
  3. 适用并发量小的网络应用开发;

不适用并发量大的应用:因为一个请求IO会阻塞进程,所以,得为每请求分配一个处理进程(线程)以及时响应,系统开销大。

非阻塞IO模型

在这里插入图片描述
进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回进程。

对于上面的阻塞IO模型来说,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。

典型应用:socket是非阻塞的方式(设置为NONBLOCK)

特点:

  1. 进程轮询(重复)调用,消耗CPU的资源;
  2. 实现难度低、开发应用相对阻塞IO模式较难;
  3. 适用并发量较小、且不需要及时响应的网络应用开发;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值