网络
OSI开放式互联参考模型——7层协议
1、物理层 传输比特流,数模转换,模数转换
2、数据链路层 比特转为帧
3、网络层 网络地址映射物理地址 例如路由器在这一层、TCP/IP中的IP协议
4、传输层 传输协议、流量控制、分割数据 例如TCP/IP中的TCP协议
5、会话层 建立、管理不同应用程序的通信
6、表示层 加密、转换翻译
7、应用层 例如TCP/IP中的HTTP协议
image.pngTCP/IP——OSI的一种实现
image.pngTCP三次握手
传输控制协议TCP简介
-
面向连接的、可靠的、基于字节流的传输层协议
-
将应用层的数据流分割成报文段并发送给目标节点的TCP层
-
数据包都有序号,对方收到则发送ACK确认,未收到则重传
-
使用校验和来检验数据在传输过程中是否有误
TCP Flags
-
URG:紧急指针标志 1 有效,0无效
-
ACK:确认序号标志 1有效,0无效
-
PSH:push标志
-
RST:重置连接标志
-
SYN:同步序号,用于建立连接过程
-
FIN:finish标志,用于释放连接
-
第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
-
第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
-
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
为什么要三次握手?
为了初始化Sequence Number的值
第一次握手隐患——SYN超时
-
Server收到Client的SYN,回复SYN-ACK的时候未收到ACK确认
-
Server不断重试直至超时,Linux默认等待63秒断开连接 默认5次,从间隔一秒开始,每次间隔翻倍
SYN Flood攻击 攻击者只发送SYN,不ACK,耗尽服务器SYN连接
防御措施
-
SYN队列满后,通过tcp_syncookies参数回发SYNCookie
-
若为正常连接则Client会回发SYNCookie,直接建立连接
建立连接后,Client出现故障怎么办?
保活机制
-
向对方发送保活探测报文,如果未收到响应则继续发送
-
尝试次数达到保活探测数仍未收到响应则中断连接
TCP四次挥手
image.png-
第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态;
-
第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态;
-
第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态;
-
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
为什么会有TIME_WAIT状态?
-
确保有足够的时间让对方收到ACK包
-
避免新旧连接混淆
为什么要四次挥手?
因为全双工,发送方和接收方都需要FIN报文和ACK报文
服务器出现大量CLOSE_WAIT状态的原因?
对方关闭socket连接,我方忙于读或写,没有及时关闭连接
-
检查代码,特别是释放资源的代码
-
检查配置,特别是处理请求的线程配置
TCP与UDP区别
UDP特点
-
面向非连接
-
不维护连接状态,支持同时向多个客户端传输相同的消息
-
数据包报头只有8个字节,额外开销较小
-
吞吐量只受限于数据生成速率、传输速率以及机器性能
-
尽最大努力交付,不保证可靠交付,不需要维持复杂的链接状态表
-
面向报文,不对应用程序提交的报文信息进行拆分或者合并
结论
-
是否面向连接
-
可靠性
-
有序性
-
速度
-
量级
TCP滑动窗口
RTT和RTO
-
RTT:发送一个数据包收到对应的ACK,所花费的时间
-
RTO:重传时间间隔
TCP使用滑动窗口做流量控制与乱序重排
-
保证TCP的可靠性
-
保证TCP的流控特性
HTTP简介
超文本传输协议HTTP主要特点
-
支持客户/服务器模式
-
简单快速
-
灵活
-
无连接
-
无状态
请求/响应的步骤
-
客户端连接到Web服务器
-
发送HTTP请求
-
服务器接受请求并返回HTTP响应
-
释放TCP连接
-
客户端浏览器解析HTML内容
在浏览器地址栏键入URL,按下回车之后经历的流程
-
DNS解析 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存
-
TCP连接
-
发送HTTP请求
-
服务器处理请求并返回HTTP报文
-
浏览器解析渲染页面
-
连接结束
HTTP状态码
-
1xx:指示信息——表示请求已接收,继续处理
-
2xx:成功——表示请求已被成功接收、理解、接受
-
3xx:重定向——要完成请求必须进行更进一步的操作
-
4xx:客户端错误——请求有语法错误或请求无法实现
-
5xx:服务器端错误——服务器未能实现合法的请求
常见状态码
-
200 OK:正常返回信息
-
400 Bad Request:客户端请求有语法错误,不能被服务器所理解
-
401 Unauthorized:请求未经授权,这个状态码必须和WWW-Authenticate报头域一起使用
-
403 Forbidden:服务器收到请求,但是拒绝提供服务
-
404 Not Found:请求资源不存在
-
500 Internal Server Error:服务器发生不可预期的错误
-
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
GET请求和POST请求的区别
从三个层面来解答
-
Http报文层面:GET将请求信息放在URL,POST放在报文体中
-
数据库层面:GET符合幂等性和安全性,POST不符合
-
其他层面:GET可以被缓存、被存储,而POST不行
Cookie和Session的区别
Cookie
-
是由服务器发送给客户端的特殊信息,以文本的形式存放在客户端
-
客户端再次请求的时候,会把Cookie回发
-
服务器接收到后,会解析Cookie生成与客户端相对应的内容
Session
-
服务器端机制,在服务器上保存的信息
-
解析客户端请求并操作session id,按需要保存状态信息
Session的实现方式
-
使用Cookie来实现
-
使用URL回写来实现
结论
-
Cookie数据存放在客户浏览器上,Session数据放在服务器上
-
Session相对于Cookie更安全
-
若考虑减轻服务器负担,应当使用Cookie
HTTP和HTTPS的区别
SSL(Security Sockets Layer,安全套接层)
-
为网络通信提供安全及数据完整性的一种安全协议
-
是操作系统对外的API,SSL3.0后更名为TLS
-
采用身份验证和数据加密保证网络通信的安全和数据的完整性
加密方式
-
对称加密:加密和解密都使用同一个密钥
-
非对称加密:加密使用的密钥和解密使用的密钥是不同的
-
哈希算法:将任意长度的信息转换为固定长度的值,算法不可逆
-
数字签名:证明某个消息或者文件是某个人发出/认同的
HTTPS数据传输流程
-
浏览器将支持的加密算法信息发送给服务器
-
服务器选择一套浏览器支持的加密算法,以证书的形式回发浏览器
-
浏览器验证证书合法性,并结合证书公钥加密信息发送给服务器
-
服务器使用私钥解密信息,验证哈希,加密响应消息回发浏览器
-
浏览器解密响应消息,并对消息进行验真,之后进行加密交互数据
结论
-
HTTPS需要到CA申请证书,HTTP不需要
-
HTTPS密文传输,HTTP明文传输
-
连接方式不同,HTTPS默认使用443端口,HTTP使用80端口
-
HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全
Socket简介
Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口
Socket通信流程
image.png
public class TCPClient {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("127.0.0.1",65000);
OutputStream os = socket.getOutputStream();
InputStream is = socket.getInputStream();
os.write(new String("Hello").getBytes());
byte[] buff = new byte[1024];
int length = is.read(buff);
String msg = new String(buff,0,length);
System.out.println("客户端收到:" + msg);
os.close();
is.close();
socket.close();
}
}
public class TCPServer {
public static void main(String[] args) throws Exception{
ServerSocket socket = new ServerSocket(65000);
while(true){
Socket ss = socket.accept();
InputStream is = ss.getInputStream();
OutputStream os = ss.getOutputStream();
byte[] buff = new byte[1024];
int length = is.read(buff);
String msg = new String(buff,0,length);
System.out.println("服务器收到:" + msg);
os.write(new String("OK").getBytes());
os.close();
is.close();
ss.close();
}
}
}