netty
2021-03-29 22:28:19 56 举报
AI智能生成
netty知识图谱
作者其他创作
大纲/内容
网络编程基础
OSI七层模型
应用层
网络服务与最终用户的一个接口
表示层
数据格式化,代码转换,数据加密
会话层
建立、管理、终止会话
传输层
定义传输数据的协议端口号,以及流量控制和差错校验
不同子网间速率不同,需要平衡
网络层
路由选择和中继,在一条数据链路上复用多条网络连接
数据链路层
数据链路的建立,拆除,对数据的检错,
纠错是数据链路层的基本任务
纠错是数据链路层的基本任务
物理层
开放系统中利用物理媒体实现物理连接的功能描述和执行连接的规程
光纤,网线,4G,5G
DTE->DCE------DCE->DTE
提供数据传输的实际通道
传输数据
TCP/IP协议
TCP:面向连接的、可靠的协议,通过三次握手建立连接,通讯完成时要拆除连接
UDP:面向无连接的通讯协议,不影响接收方确认,属于不可靠的传输,可能会丢包
三次握手
为什么不是两次
第一次握手,确认客户端的发送能力、服务端的接收能力
第二次握手,客户端就确认了服务端的接收、发送能力,服务端确认了客户端的发送能力
但是服务器并不能确认客户端的接收能力
但是服务器并不能确认客户端的接收能力
第三次握手,互相确认了对方的发送接收能力
半连接队列
服务器第一次收到客户端的SYN后,就会处于SYN_RCVD状态,
服务器会把此种状态下请求连接放在一个队列里
服务器会把此种状态下请求连接放在一个队列里
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,
等待一段时间仍未收到客户确认包,进行第二次重传。
如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除,
重传时间指数级增长
等待一段时间仍未收到客户确认包,进行第二次重传。
如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除,
重传时间指数级增长
第三次握手的时候,是可以携带数据
SYN攻击
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的
所以服务器容易受到SYN洪泛攻击
所以服务器容易受到SYN洪泛攻击
SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,
Server则回复确认包,并等待Client确认,由于源地址不存在,
因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,
导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪
Server则回复确认包,并等待Client确认,由于源地址不存在,
因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,
导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪
linux检测 netstat -n -p TCP | grep SYN_RECV
防御方法
缩短超时时间
增加最大半连接数
过滤网关防护
SYN cookies
ISN可以看作是一个32比特的计数器,每4ms加1
四次挥手
ACK报文是用来应答的,SYN报文是用来同步的
报文段最大生存时间MSL
2MSL等待时间原因:让TCP再次发送最后的ACK以防这个ACK丢失
一次HTTP请求过程
1.建立TCP连接
2.客户端向服务器发送请求命令
3.客户端发送请求头信息
4.服务器应答
5.返回响应头信息
6.服务器向客户端发送数据
7.服务器关闭TCP连接,keep-alive
IO模型
同步
阻塞I/O(blockingI/O)
非阻塞I/O(nonblockingI/O)
I/O复用(select 、poll和epoll) (I/O multiplexing)
信号驱动I/O (signal driven I/O (SIGIO))
异步
异步I/O (asynchronous I/O ) 伪IO,windows IOCP
JDK IO
BIO
概述
每和一个客户端建立一个连接, 就会开启一个线程去进行读写,没有数据读写时,该线程阻塞
流程
1.服务器端启动一个serverSocket
2.客户端启动Socket对服务器进行通信,默认情况下服务器端需要对每个客户建立一个线程与之通信
3.客户端发出请求后,先咨询服务器是否有线程响应,没有则等待或被拒绝
4.如果有响应,客户端线程会等待请求结束后,再继续执行读取或写入数据
实现
读取
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))
写入
pw = new PrintWriter(socket.getOutputStream())
pw.println("");
pw.flush();
pw.println("");
pw.flush();
NIO
概述
创建一个选择器去轮询客户端的事件,然后交给工作线程去执行,多路复用线程
核心
通道Channel
BIO中是单向的,而NIO中是双向的
常见
FileChannel
用于文件的读写
read(ByteBuffer dst)
从通道读取数据并放到缓冲区
从通道读取数据并放到缓冲区
write(ByteBuffer src)
把缓冲区的数据写到通道中
把缓冲区的数据写到通道中
transferFrom 从目标通道中复制数据到当前通道
transferTo
把数据从当前通道复制给目标通道
把数据从当前通道复制给目标通道
DatagramChannel
用于UDP的数据读写
ServerSocketChannel
用于TCP的读写
当客户端连接上来时会生成一个scoketChannel用来读写数据
SocketChannel
同上
缓冲区Buffer
选择器Selector
Netty的IO线程NioEventLoop聚合了selcetor
通道没有数据读写时,会切换到另一个通道,没用上下文的切换
selectNow
select()
select(timeout)
wakeup
SelectionKey
OP_READ=1<<0
OP_WRITE=1<<2
OP_CONNECT=1<<3
OP_ACCEPT=1<<4
方法
select()获取selector选择器
channel()获取对应的通道,强转socketChannel
attachment()与之相关的附件
interestOps()改变监听事件
4个is方法,可读,可写,可连接,可接受连接
原理
1.服务器端ServerSocketChannel,客户端SocketChannel
服务器端会根据客户端的连接创建对应的SocketChannel
服务器端会根据客户端的连接创建对应的SocketChannel
2.服务器端ServerSocketChannel和生成的SocketChannel都需要注册Selector选择器中,
并且都需要设置非阻塞,服务器端注册为OP_ACCEPT事件
并且都需要设置非阻塞,服务器端注册为OP_ACCEPT事件
3.selector循环监听是否有事件(selectionKey)发生,
有则根据类型进行处理,处理完则从集合中移除,采用迭代器
有则根据类型进行处理,处理完则从集合中移除,采用迭代器
isAcceptable
serverSocketChannel.accept得到socketChannel,并且设置非阻塞
将socketChannel注册到selector中,读取事件
isReadable
获取selectionKey中的channel,强转为socketChannel,然后读取其中的数据
isWritable
isConnectable
代码
实现
Selector selector = Selector.open()
client
初始化
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.configureBlocking(false);
连接服务器
boolean connect = socketChannel.connect(new InetSocketAddress(host, port));
if (!connect) {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
if (!connect) {
socketChannel.register(selector, SelectionKey.OP_CONNECT);
}
工作
key.isValid
key.isConnectable()
socketChannel.finishConnect()
socketChannel.finishConnect()
key.isReadable(),调用读取
server
初始化
ServerSocketChannel serverChannel= ServerSocketChannel.open()
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
工作
key.isValid
key.isAcceptable,接收连接
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
SocketChannel sc = channel.accept();
sc.configureBlocking(false);
sc.register(selector, SelectionKey.OP_READ);
key.isReadable(),调用读取
读取
写入
零拷贝
NIO中transferTo
NIO中transferTo
拷贝
DMA copy
direct memory access copy
从硬盘拷贝到直接内存中
cpu copy
传统io
mmap(内存映射)
sendFile
对比
mmap适合小数据量读写,sendFile适合大文件传输
mmap需要4次上下文切换,3次数据拷贝,sendFile 3次切换,2次数据拷贝
AIO
概述
实现
client
server
读取
写入
byte[] bytes = msg.getBytes();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
writeBuffer.put(bytes);
writeBuffer.flip();
ByteBuffer
ByteBuffer.allocate
如果开辟的空间比要发送的数据长度下会发生BufferOverflowException
如果服务器端byteBuffer获取的长度不是byteBuffer.remaining
如果小于,取出的数据会丢失一部分,大于会抛出异常BufferUnderflowException
如果小于,取出的数据会丢失一部分,大于会抛出异常BufferUnderflowException
有可能无法一次性将数据写完,需要检查缓冲区中是否还有数据需要继续进行网络写
if(byteBuffer.hasRemaining()){
channel.write(byteBuffer,byteBuffer,this);
}
if(byteBuffer.hasRemaining()){
channel.write(byteBuffer,byteBuffer,this);
}
CompletionHandler
位操作
与&
都1为1
或|
有1为1
取反~
按位取反
异或^
同0非1
右移>>
右移>>
除以2的n次 各二进位全部右移若干位,低位丢弃,高位补符号位补0(符号位负补1正补0)
无符号右移>>>
除以2的n次 各二进位全部右移若干位,高位补零,低位丢弃
左移<<
左移<<
乘以2的n次 各二进位全部左移若干位,高位丢弃,低位补0
无符号左移是不存在的
Netty核心
Bootstrap/ServerBootstrap
netty简单配置或者引导程序的类
Bootstrap用来连接远程主机,有1个EventLoopGroup
ServerBootstrap用来绑定本地端口,有2个EventLoopGroup
一个用来接收建立连接,一个用来数据传输
一个用来接收建立连接,一个用来数据传输
EventLoop
EventLoopGroup
ChannelPipeline
ChannelPipeline和EventLoop和EventLoopGroup密切相关,因为它们三个都和事件处理相关
Channel
Future/ChannelFuture
ChannelInitializer
用来配置Handlers,通过ChannelPipeline来添加ChannelHandler
如发送和接收消息,这些Handlers将确定发的是什么消息。
ChannelInitializer自身也是一个ChannelHandler,在添加完其他的handlers之后会自动从ChannelPipeline中删除自己
ChannelInitializer自身也是一个ChannelHandler,在添加完其他的handlers之后会自动从ChannelPipeline中删除自己
ChannelHandler
(类)ChannelHandlerAdapter
子类
(类)ChannelInboundHandlerAdapter
(类)ChannelOutboundHandlerAdapter
(接口)ChannelInboundHandler
用于接收消息,处理消息
需要返回消息时可以write/flush数据
(接口)ChannelOutboundHandler
Netty应用
服务模型
传统阻塞IO模型
采用阻塞IO模式获取输入的数据
每个连接都需要独立的线程完成数据的输入,业务处理,返回
缺点
并发数大,创建大量线程,占用资源多
没有数据读取,会阻塞在read操作,造成线程资源浪费
Reactor
解决传统IO缺点
基于IO复用模型
基于线程池复用线程资源
三种实现
单Reactor单线程
优点
模型简单,没有多线程,进程通信,竞争的问题
缺点
单线程,无法发挥多核CPU性能,handler在处理某个连接的业务时无法处理其他连接
可靠行问题,节点故障
单Reactor多线程
增加了handler来响应时间,然后交给线程池工作线程来处理
优点:利用多核CPU的处理能力
缺点
多线程数据共享和访问比较复杂
reactor处理所有的事件的监听和响应,单线程运行,
在高并发场景容易出现性能瓶颈
在高并发场景容易出现性能瓶颈
主从Reactor多线程
优点
父线程和子线程数据交互简单明确
缺点:编程复杂度高
Netty模型
基于主从Reactor多线程模型改进
taskQueue
耗时长的任务,异步提交给队列
ctx.channel().eventLoop().execute(()->{})
定时任务,异步提交给队列
ctx.channel().eventLoop().schedule(()->{},5,TimeUnit)
Future-listener机制
ChannelFuture.addListener();
添加监听器,执行后回调该方法
添加监听器,执行后回调该方法
0 条评论
下一页