每次面试都问我TCP、UDP、Socket、HTTP、IO、Netty、RPC等网络编程,宝宝很不开心
文章非常长!!! 非常详细!!!,此次网络编程系列分为很多篇文章,后续补齐
同系列的文章地址为:
超硬核!!!一篇文章搞定BIO、NIO、AIO、Netty(详细基础内容+网络编程内容+代码示例)【网络编程 2】
第一场:
面试官:你说一下TCP的三次握手
我:第一次Client将SYN置1…、第二次Server收…、 第三次…
面试官:很难背吧?
我:…是啊,很难,要不我在和你说说四次挥手?
面试官:别了别了回去等通知吧…
我:"…"
第二场:
心里憋了一万个草泥马来到的第二家
…
面试官:你说一下TCP的三次握手
我(心里在想,还来?
):没什么好说的,就是为了保持一次网络通信交互正常
面试官:你能说的清楚一点吗?
我:就等于是你在不认识我的情况下打我的电话让我来面试
面试官:“懵了”,好像是这么回事
面试官:你说一下TCP的四次挥手
我:等于是我在上家公司辞职了
面试官:“想了一下”,能不能说的清楚一点吗?
我:我找老板办理离职,老板说可以,老板接着给我办理离职,我才可以走
面试官:有道理!
面试官:你说一下TCP和UDP的区别吧
我:TCP等于和陌生人打电话处理事情,UDP等于发广播
面试官:“…”有道理
面试官:你期望薪资多少
我:15K
面试官:下周一有时间入职?
我:…也懵了,面试到底是要怎样面试
接着进入正题
网络编程的本质是多台计算机之间的数据交换。数据传递本身没有多大的难度,不就是把一个设备中的数据发送给其他设备,然后接受另外一个设备反馈的数据。现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个,然后接收另一个设备的反馈。在网络编程中,发起连接程序,也就是发送第一次请求的程序,被称作客户端(Client),等待其他程序连接的程序被称作服务器(Server)。客户端程序可以在需要的时候启动,而服务器为了能够时刻相应连接,则需要一直启动。
例如以打电话为例,首先拨号的人类似于客户端,接听电话的人必须保持电话畅通类似于服务器。连接一旦建立以后,就客户端和服务器端就可以进行数据传递了,而且两者的身份是等价的。在一些程序中,程序既有客户端功能也有服务器端功能,最常见的软件就是QQ、微信这类软件了。
在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可以唯一地确定Internet上的一台主机。
而TCP层则提供面向应用的可靠(TCP)的或非可靠(UDP)的数据传输机制,这是网络编程的主要对象,一般不需要关心IP层是如何处理数据的。
目前较为流行的网络编程模型是客户机/服务器(C/S)结构。即通信双方一方作为服务器等待客户提出请求并予以响应。客户则在需要服务时向服务器提 出申请。服务器一般作为守护进程始终运行,监听网络端口,一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的客户也 能及时得到服务。
TCP/IP参考模型
TCP/IP四层协议(数据链路层、网络层、传输层、应用层)
硬件
和软件加到物理线路,这样就构成了数据链路,TCP/IP即传输控制/网络协议,是面向连接的协议,发送数据前要先建立连接(发送方和接收方的成对的两个之间必须建 立连接),TCP提供可靠的服务,也就是说,通过TCP连接传输的数据不会丢失,没有重复,并且按顺序到达
UDP它是属于TCP/IP协议族中的一种。是无连接的协议,发送数据前不需要建立连接,是没有可靠性的协议。因为不需要建立连接所以可以在在网络上以任何可能的路径传输,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。
TCP是面向连接的协议,发送数据前要先建立连接,TCP提供可靠的服务,也就是说,通过TCP连接传输的数据不会丢失,没有重复,并且按顺序到达;
UDP是无连接的协议,发送数据前不需要建立连接,是没有可靠性;
TCP通信类似于于要打个电话,接通了,确认身份后,才开始进行通行;
UDP通信类似于学校广播,靠着广播播报直接进行通信。
TCP只支持点对点通信,UDP支持一对一、一对多、多对一、多对多;
TCP是面向字节流的,UDP是面向报文的;
面向字节流是指发送数据时以字节为单位,一个数据包可以拆分成若干组进行发送,而UDP一个报文只能一次发完。
TCP首部开销(20字节)比UDP首部开销(8字节)要大
UDP 的主机不需要维持复杂的连接状态表
TCP通信可看作打电话:
李三(拨了个号码):喂,是王五吗?
王五:哎,您谁啊?
李三:我是李三,我想给你说点事儿,你现在方便吗?
王五:哦,我现在方便,你说吧。
甲:那我说了啊?
乙:你说吧。
(连接建立了,接下来就是说正事了…)
UDP通信可看为学校里的广播:
播音室:喂喂喂!全体操场集合
运行在TCP协议上的协议:
运行在UDP协议上的协议:
运行在TCP和UDP协议上:
ARP协议完成了IP地址与物理地址的映射。每一个主机都设有一个 ARP 高速缓存,里面有所在的局域网上的各主机和路由器的 IP 地址到硬件地址的映射表。当源主机要发送数据包到目的主机时,会先检查自己的ARP高速缓存中有没有目的主机的MAC地址,如果有,就直接将数据包发到这个MAC地址,如果没有,就向所在的局域网发起一个ARP请求的广播包(在发送自己的 ARP 请求时,同时会带上自己的 IP 地址到硬件地址的映射),收到请求的主机检查自己的IP地址和目的主机的IP地址是否一致,如果一致,则先保存源主机的映射到自己的ARP缓存,然后给源主机发送一个ARP响应数据包。源主机收到响应数据包之后,先添加目的主机的IP地址与MAC地址的映射,再进行数据传送。如果源主机一直没有收到响应,表示ARP查询失败。
如果所要找的主机和源主机不在同一个局域网上,那么就要通过 ARP 找到一个位于本局域网上的某个路由器的硬件地址,然后把分组发送给这个路由器,让这个路由器把分组转发给下一个网络。剩下的工作就由下一个网络来做。
网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket。Socket通常用来实现客户方和服务方的连接。Socket是TCP/IP协议的一个十分流行的编程界面,一个Socket由一个IP地址和一个端口号唯一确定。
但是,Socket所支持的协议种类也不光TCP/IP、UDP,因此两者之间是没有必然联系的。在Java环境下,Socket编程主要是指基于TCP/IP协议的网络编程。
socket连接就是所谓的长连接,客户端和服务器需要互相连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉的,但是有时候网络波动还是有可能的
基于TCP:服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。
基于UDP:UDP 协议是用户数据报协议的简称,也用于网络数据的传输。虽然 UDP 协议是一种不太可靠的协议,但有时在需要较快地接收数据并且可以忍受较小错误的情况下,UDP 就会表现出更大的优势。我客户端只需要发送,服务端能不能接收的到我不管
先运行服务端,在运行客户端
,
package com.test.io;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
//TCP协议Socket使用BIO进行通行:服务端
public class BIOServer {
// 在main线程中执行下面这些代码
public static void main(String[] args) {
//1单线程服务
ServerSocket server = null;
Socket socket = null;
InputStream in = null;
OutputStream out = null;
try {
server = new ServerSocket(8000);
System.out.println("服务端启动成功,监听端口为8000,等待客户端连接...");
while (true){
socket = server.accept(); //等待客户端连接
System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress());
in = socket.getInputStream();
byte[] buffer = new byte[1024];
int len = 0;
//读取客户端的数据
while ((len = in.read(buffer)) > 0) {
System.out.println(new String(buffer, 0, len));
}
//向客户端写数据
out = socket.getOutputStream();
out.write("hello!".getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.test.io;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
//TCP协议Socket:客户端
public class Client01 {
public static void main(String[] args) throws IOException {
//创建套接字对象socket并封装ip与port
Socket socket = new Socket("127.0.0.1", 8000);
//根据创建的socket对象获得一个输出流
OutputStream outputStream = socket.getOutputStream();
//控制台输入以IO的形式发送到服务器
System.out.println("TCP连接成功 \n请输入:");
while(true){
byte[] car = new Scanner(System.in).nextLine().getBytes();
outputStream.write(car);
System.out.println("TCP协议的Socket发送成功");
//刷新缓冲区
outputStream.flush();
}
}
}
先运行服务端,在运行客户端
//UDP协议Socket:服务端
public class Server1 {
public static void main(String[] args) {
try {
//DatagramSocket代表声明一个UDP协议的Socket
DatagramSocket socket = new DatagramSocket(8888);
//byte数组用于数据存储。
byte[] car = new byte[1024];
//DatagramPacket 类用来表示数据报包DatagramPacket
DatagramPacket packet = new DatagramPacket(car, car.length);
// //创建DatagramPacket的receive()方法来进行数据的接收,等待接收一个socket请求后才执行后续操作;
System.out.println("等待UDP协议传输数据");
socket.receive(packet);
//packet.getLength返回将要发送或者接收的数据的长度。
int length = packet.getLength();
System.out.println("啥东西来了:" + new String(car, 0, length));
socket.close();
System.out.println("UDP协议Socket接受成功");
} catch (IOException e) {
e.printStackTrace();
}
}
}
//UDP协议Socket:客户端
public class Client1 {
public static void main(String[] args) {
try {
//DatagramSocket代表声明一个UDP协议的Socket
DatagramSocket socket = new DatagramSocket(2468);
//字符串存储人Byte数组
byte[] car = "UDP协议的Socket请求,有可能失败哟".getBytes();
//InetSocketAddress类主要作用是封装端口
InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
//DatagramPacket 类用来表示数据报包DatagramPacket
DatagramPacket packet = new DatagramPacket(car, car.length, address);
//send() 方法发送数据包。
socket.send(packet);
System.out.println("UDP协议的Socket发送成功");
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
类名 | 用于 | 作用 |
---|---|---|
Socket | TCP协议 | Socket类同时工作于客户端和服务端,所有方法都是通用的,这个类三个主要作用,校验包信息,发起连接(Client),操作流数据(Client/Server) |
ServerSocket | TCP协议 | ServerSocket表示为服务端,主要作用就是绑定并监听一个服务器端口,为每个建立连接的客户端“克隆/映射”一个Socket对象,具体数据操作都是通过这个Socket对象完成的,ServerSocket只关注如何和客户端建立连接 |
DatagramSocket | ODP协议 | DatagramSocket 类用于表示发送和接收数据报包的套接字。 |
DatagramPacket | ODP协议 | DatagramPacket 类用来表示数据报包,数据报包用来实现无连接包投递服务。 |
InetAddress | IP+端口号 | Java提供了InetAddress类来代表互联网协议(IP)地址,InetAddress类没有提供构造器,而是提供了如下两个静态方法来获取InetAddress实例: |
InetSocketAddress | IP+端口号 | 在使用Socket来连接服务器时最简单的方式就是直接使用IP和端口,但Socket类中并未提供这种方式,而是靠SocketAddress的子类InetSocketAddress来实现 IP 地址 + 端口号的创建,不依赖任何协议。 |
构造方法 | 描述 |
---|---|
public ServerSocket(SocketImpl impl) | ServerSocket套接字的实际工作由SocketImpl类的实例执行 |
public ServerSocket(int port) | 指定端口来绑定服务器套接字 |
public ServerSocket(int port, int backlog) | 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号 |
public ServerSocket(int port, int backlog, InetAddress address) | 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器 |
public ServerSocket() | 创建非绑定服务器套接字 |
实际方法 | 描述 |
---|---|
public int getLocalPort() | 返回此套接字在其上侦听的端口 |
public Socket accept() | 侦听并接受到此套接字的连接,信息和数据都是通过这个Socket对象获取的 |
public void setSoTimeout(int timeout) | 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位 |
public void bind(SocketAddress host, int backlog) | 将 ServerSocket 绑定到特定地址(IP 地址和端口号) |
构造方法 | 描述 |
---|---|
public Socket(String host, int port) | 创建一个流套接字并将其连接到指定主机上的指定端口号 |
public Socket(InetAddress host, int port) | 创建一个流套接字并将其连接到指定 IP 地址的指定端口号 |
public Socket(String host, int port, InetAddress localAddress, int localPort) | 创建一个套接字并将其连接到指定远程主机上的指定远程端口 |
public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) | 创建一个套接字并将其连接到指定远程地址上的指定远程端口 |
public Socket() | 通过系统默认类型的 SocketImpl 创建未连接套接字 |
实际方法 | 描述 |
---|---|
public void connect(SocketAddress host, int timeout) | 将此套接字连接到服务器,并指定一个超时值 |
public InetAddress getInetAddress() | 返回套接字连接的地址 |
public int getPort() | 返回此套接字连接到的远程端口 |
public int getLocalPort() | 返回此套接字绑定到的本地端口 |
public SocketAddress getRemoteSocketAddress() | 返回此套接字连接的端点的地址,如果未连接则返回 null |
public InputStream getInputStream() | 返回此套接字的输入流,常使用DataOutputStream做容器 |
public OutputStream getOutputStream() | 返回此套接字的输出流,常用DataOutputStream |
public void close() | 关闭此套接字,来自Closeable |
构造方法 | 描述 |
---|---|
DatagramSocket() | 构造数据报包套接字并将其绑定到本地主机上任何可用的端口。 |
DatagramSocket(int port) | 创建数据报包套接字并将其绑定到本地主机上的指定端口。 |
DatagramSocket(int portJnetAddress addr) | 创建数据报包套接字,将其绑定到指定的本地地址。 |
DatagramSocket(SocketAddress bindaddr) | 创建数据报包套接字,将其绑定到指定的本地套接字地址。 |
实际方法 | 描述 |
---|---|
void bind(SocketAddress addr) | 将此 DatagramSocket 绑定到特定的地址和端口。 |
void close() | 关闭此数据报包套接字。 |
void connect(InetAddress address,int port) | 将套接字连接到此套接字的远程地址。 |
void connect(SocketAddress addr) | 将此套接子连接到远程套接子地址(IP地址+端口号)。 |
void disconnect() | 断开套接字的连接。 |
InetAddress getInetAddress() | 返回此套接字连接的地址。 |
InetAddress getLocalAddress() | 获取套接字绑定的本地地址。 |
int getLocalPort() | 返回此套接字绑定的本地主机上的端口号。 |
int getPort() | 返回此套接字的端口。 |
构造方法 | 描述 |
---|---|
DatagramPacket(byte[] buf,int length) | 构造 DatagramPacket,用来接收长度为 length 的数据包。 |
DatagramPacket(byte[] buf,int offset, int length) | 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。 |
DatagramPacket(byte[] buf,int length,InetAddress address,int port) | 构造 DatagramPacket,用来将长度为 length 的包发送到指定主机上的指定端口。 |
DatagramPacket(byte[] buf,int length,SocketAddress address) | 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口。 |
DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port) | 构造 DatagramPacket,用来将长度为 length 偏移量为 offset的包发送到指定主机上的指定端口。 |
DatagramPacket(byte[] buf,int offset,int length,SocketAddress address) | 构造数据报包,用来将长度为 length、偏移量为 offset 的包发送到指定主机上的指定端口。 |
实际方法 | 描述 |
---|---|
InetAddress getAddress() | 返回某台机器的 IP 地址,此数据报将要发往该机器或者从该机器接收。 |
byte[] getData() | 返回数据缓冲区。 |
int getLength() | 返回将要发送或者接收的数据的长度。 |
int getOffset() | 返回将要发送或者接收的数据的偏移量。 |
int getPort() | 返回某台远程主机的端口号,此数据报将要发往该主机或者从该主机接收。 |
getSocketAddress() | 获取要将此包发送或者发出此数据报的远程主机的SocketAddress(通常为 IP地址+端口号)。 |
void setAddress(InetAddress addr) | 设置要将此数据报发往的目的机器的IP地址。 |
void setData(byte[] buf) | 为此包设置数据缓冲区。 |
void setData(byte[] buf,int offset,int length) | 为此包设置数据缓冲区。 |
void setLength(int length) | 为此包设置长度。 |
void setPort(int port) | 设置要将此数据报发往的远程主机的端口号。 |
void setSocketAddress(SocketAddress address) | 设置要将此数据报发往的远程主机的SocketAddress (通常为 IP地址+端口号)。 |
实际方法 | 描述 |
---|---|
getLocalHost() | 获得一个InetAddress对象,该对象含有本地机的域名和IP地址。 |
getByName(String host) | 根据主机名获取对应的InetAddress对象。 |
getByAddress(byte[] addr) | 根据原始IP地址来获取对应的InetAddress对象 |
getAddress() | 获取字节数组形式的IP地址 |
代码示例:
package com.lijie;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws UnknownHostException {
//InetAddress.getLocalHost();该对象含有本地机的域名和IP地址
InetAddress address = InetAddress.getLocalHost();
System.out.println("计算机名" + address.getHostName());
System.out.println("IP地址" + address.getHostAddress());
//根据机器名获取InetAddress实例
InetAddress address2 = InetAddress.getByName("MS-CFPIYPGYRLOM");
System.out.println("IP地址" + address2.getHostAddress());
//根据ip地址获取InetAddress实例
InetAddress address3 = InetAddress.getByName("192.168.157.1");
System.out.println("计算机名" + address3.getHostName());
}
}
InetSocketAddress是SocketAddress的实现子类。此类实现 IP 地址(IP 地址 + 端口号)的创建,不依赖任何协议。
在使用Socket来连接服务器时最简单的方式就是直接使用IP和端口,但Socket类中并未提供这种方式,而是靠SocketAddress的子类InetSocketAddress来实现 IP 地址 + 端口号的创建,不依赖任何协议。
//使用方式
SocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
SocketAddress address2 = new InetSocketAddress("120.77.0.58", 0000);
Socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;
Socket适用场景:网络游戏,银行持续交互,直播,在线视屏等。
http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断开等待下次连接
http适用场景:公司OA服务,互联网服务,电商,办公,网站等等等等
其实HTTPS就是从HTTP加上加密处理(一般是SSL安全通信线路)+认证+完整性保护
区别:
HTTP通信机制是在一次完整的HTTP通信过程中,Web浏览器与Web服务器之间将完成下列7个步骤:
建立TCP连接
怎么建立连接的,看上面的三次捂手
Web浏览器向Web服务器发送请求行
一旦建立了TCP连接,Web浏览器就会向Web服务器发送请求命令。例如:GET /sample/hello.jsp HTTP/1.1。
Web浏览器发送请求头
浏览器发送其请求命令之后,还要以头信息的形式向Web服务器发送一些别的信息,之后浏览器发送了一空白行来通知服务器,它已经结束了该头信息的发送。
Web服务器应答
客户机向服务器发出请求后,服务器会客户机回送应答, HTTP/1.1 200 OK ,应答的第一部分是协议的版本号和应答状态码。
Web服务器发送应答头
正如客户端会随同请求发送关于自身的信息一样,服务器也会随同应答向用户发送关于它自己的数据及被请求的文档。
Web服务器向浏览器发送数据
Web服务器向浏览器发送头信息后,它会发送一个空白行来表示头信息的发送到此为结束,接着,它就以Content-Type应答头信息所描述的格式发送用户所请求的实际数据。
Web服务器关闭TCP连接
HTTP状态码表示客户端HTTP请求的返回结果、标识服务器处理是否正常、表明请求出现的错误等。
状态码的类别:
类别 | 描述 |
---|---|
1xx: | 指示信息–表示请求已接收,正在处理 |
2xx: | 成功–表示请求已被成功接收、理解、接受 |
3xx: | 重定向–要完成请求必须进行更进一步的操作 |
4xx: | 客户端错误–请求有语法错误或请求无法实现 |
5xx: | 服务器端错误–服务器未能实现合法的请求 |
状态码 | 描述 |
---|---|
200: | 请求被正常处理 |
204: | 请求被受理但没有资源可以返回 |
206: | 客户端只是请求资源的一部分,服务器只对请求的部分资源执行GET方法,相应报文中通过Content-Range指定范围的资源。 |
301: | 永久性重定向 |
302: | 临时重定向 |
303: | 与302状态码有相似功能,只是它希望客户端在请求一个URI的时候,能通过GET方法重定向到另一个URI上 |
304: | 发送附带条件的请求时,条件不满足时返回,与重定向无关 |
307: | 临时重定向,与302类似,只是强制要求使用POST方法 |
400: | 请求报文语法有误,服务器无法识别 |
401: | 请求需要认证 |
403: | 请求的对应资源禁止被访问 |
404: | 服务器无法找到对应资源 |
500: | 服务器内部错误 |
503: | 服务器正忙 |
请求方式 | 描述 |
---|---|
GET: | 用于请求访问已经被URI(统一资源标识符)识别的资源,可以通过URL传参给服务器 |
POST: | 用于传输信息给服务器,主要功能与GET方法类似,但一般推荐使用POST方式。 |
PUT: | 传输文件,报文主体中包含文件内容,保存到对应URI位置。 |
HEAD: | 获得报文首部,与GET方法类似,只是不返回报文主体,一般用于验证URI是否有 效。 |
PATCH: | 客户端向服务器传送的数据取代指定的文档的内容(部分取代) |
TRACE: | 回显客户端请求服务器的原始请求报文,用于"回环"诊断 |
DELETE: | 删除文件,与PUT方法相反,删除对应URI位置的文件。 |
OPTIONS: | 查询相应URI支持的HTTP方法。 |
区别一:
get重点在从服务器上获取资源,post重点在向服务器发送数据;
区别二:
Get传输的数据量小,因为受URL长度限制,但效率较高;
Post可以传输大量数据,所以上传文件时只能用Post方式;
区别三:
get是不安全的,因为get请求发送数据是在URL上,是可见的,可能会泄露私密信息,如密码等;
post是放在请求头部的,是安全的
HTTP1.0版本的特性:
HTTP1.1版本新特性
HTTP2.0版本的特性
对称密钥加密是指加密和解密使用同一个密钥的方式,这种方式存在的最大问题就是密钥发送问题,即如何安全地将密钥发给对方;
而非对称加密是指使用一对非对称密钥,即公钥和私钥,公钥可以随意发布,但私钥只有自己知道。发送密文的一方使用对方的公钥进行加密处理,对方接收到加密信息后,使用自己的私钥进行解密。
由于非对称加密的方式不需要发送用来解密的私钥,所以可以保证安全性;但是和对称加密比起来,非常的慢
session 是浏览器和服务器会话过程中,服务器会分配的一块储存空间给session。
服务器默认为客户浏览器的cookie中设置 sessionid,这个sessionid就和cookie对应,浏览器在向服务器请求过程中传输的cookie 包含 sessionid ,服务器根据传输cookie 中的 sessionid 获取出会话中存储的信息,然后确定会话的身份信息。
session 是浏览器和服务器会话过程中,服务器会分配的一块储存空间给session。
服务器默认为客户浏览器的cookie中设置 sessionid,这个sessionid就和cookie对应,浏览器在向服务器请求过程中传输的cookie 包含 sessionid ,服务器根据传输cookie 中的 sessionid 获取出会话中存储的信息,然后确定会话的身份信息。