描述
网络编程中传输的数据总是具有相同的类型: 字节,这些字节是如何流动的主要取决于我们所说的网络传输
一个帮助我们抽象底层的数据传输机制的概念,
在网络编程中主要的传输有 OIO-阻塞传输,NIO-异步传输,Local-JVM内部的异步通信,Netty-异步通信
在实现这种编程的转换的时候,比如 阻塞io传输到 nio异步传输,的时候,你会发现代码往往不能进行复用。并且
自己可能要重新编写一套代码来解决问题,然而Netty 为他所有的传输实现了提供了一个通用的API,这相较于JDK
所提供的简单的很多,接下来我们就来编写一下代码来看看,Netty 是如何方便。
代码实例
OIO 阻塞传输:
/**
* @Author qrn
* @Title https://blog.youkuaiyun.com/qq_41971087
* @Date 2021/5/24 17:35
* @time 17:35
* 使用 oio 进行 服务端与客户端连接通信,并且 发送一个简单 hi 给客户端
* 传输 实例:
*
* 1.创建 socker 服务,定义好ip 和端口
* 2.获取客户端链接accept
* 3.循环一直等待请求连接,使用 线程 ,每一个请求都创建一个线程去处理
* 4. 连接成功后,写入数据 hi到 客户端
* 5.关闭流
*/
public class PlainOioServer {
/**
* 启动server 方法
* @param port 端口:
* @throws IOException
*/
public static void server(int port) throws IOException{
final ServerSocket socket = new ServerSocket(port);
try{
for(;;){
final Socket clientSocket = socket.accept();
System.out.println("Accpted connection from "+clientSocket);
//创建线程去处理:
new Thread(new Runnable() {
@Override
public void run() {
OutputStream out;
try{
out = clientSocket.getOutputStream();
//写数据到 客户端
out.write("Hi ! \r\n".getBytes(Charset.forName("UTF-8")));
out.flush();
clientSocket.close();
}catch (IOException e){
e.printStackTrace();
}finally {
try{
clientSocket.close();
}catch (IOException ex){
// ignorte on close
}
}
}
}).start();
}
}catch (IOException e){
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception{
server(8080);
}
}
NIO 异步传输
/**
* @Author qrn
* @Title https://blog.youkuaiyun.com/qq_41971087
* @Date 2021/5/24 18:08
* @time 18:08
*
* nio 服务端传输:
*
*/
public class PlainNioServer {
public void server(int port)throws IOException{
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);// 开启非阻塞
//通过获取 socket 然后注册 端口和ip
ServerSocket ssocket = serverChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ssocket.bind(address); //绑定
//选择器,获取选择器 把通道注册进选中器中
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
final ByteBuffer msg = ByteBuffer.wrap("Hi! \r\n".getBytes());
for(;;){
try{
//等待需要处理的新事件,阻塞将一直持续下一个传入事件
selector.select();
}catch (IOException e){
e.printStackTrace();
break;
}
//获取所有接收事件的SelectionKey 实例,
//通过 SelectionKey 可以获取到1 通道,这样就可以把接收的数据
//转发给客户端。
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove();
try{
//连接事件
if(key.isAcceptable()){
ServerSocketChannel server =(ServerSocketChannel) key.channel();
SocketChannel client = server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE
|SelectionKey.OP_READ,msg.duplicate());
System.out.println("当前创建连接的客户端是:"+client);
}
//检查套接字是否已经装备好写数据
if(key.isWritable()){
SocketChannel client =(SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer)key.attachment();
while (buffer.hasRemaining()){
if(client.write(buffer) == 0){
break;
}
}
client.close();
}
}catch (IOException ex){
key.cancel();
try{
key.channel().close();
}catch (IOException cex){
//close
}
}
}
}
}
}
从上面可以看出,阻塞io传输到 nio 异步传输的代码全变了,而且编码越来越复杂,接下来我们看 Netty的代码实例。
Netty 阻塞的io模型
/**
* @Author qrn
* @Title https://blog.youkuaiyun.com/qq_41971087
* @Date 2021/5/24 19:34
* @time 19:34
* netty oio 传统阻塞io:
* OioEventLoopGroup()
* OioServerSocketChannel
* 表示当前是阻塞的io模型
*/
public class NettyOioServer {
public void server(int port) throws Exception{
final ByteBuf buf = Unpooled.unreleasableBuffer(
Unpooled.copiedBuffer("Hi! \r\n", Charset.forName("UTF-8"))
);
EventLoopGroup group = new OioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group).channel(OioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelActive(
ChannelHandlerContext ctx)
throws Exception {
ctx.writeAndFlush(buf.duplicate())
.addListener(
ChannelFutureListener.CLOSE);
}
});
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
}finally {
//释放所有资源:
group.shutdownGracefully().sync();
}
}
}
Netty 异步非阻塞io模型
/**
* @Author qrn
* @Title
* @Date 2021/5/24 19:52
* @time 19:52
* 传统非阻塞io
*
* 与传统阻塞io的区别就在于
* OioEventLoopGroup() 替换 NioEventLoopGroup
* OioServerSocketChannel 替换 NioServerSocketChannel
* 就可以了
*
*/
public class NettyNioServer {
public void server(int port) throws Exception{
final ByteBuf buf = Unpooled.unreleasableBuffer(
Unpooled.copiedBuffer("Hi! \r\n", Charset.forName("UTF-8"))
);
EventLoopGroup group = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(group).channel(NioServerSocketChannel.class)
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
@Override
public void channelActive(
ChannelHandlerContext ctx)
throws Exception {
ctx.writeAndFlush(buf.duplicate())
.addListener(
ChannelFutureListener.CLOSE);
}
});
}
});
ChannelFuture f = b.bind().sync();
f.channel().closeFuture().sync();
}finally {
//释放所有资源:
group.shutdownGracefully().sync();
}
}
}
可以看到这上面的代码基本是一致的,唯一的区别就是
- OioEventLoopGroup() 替换 NioEventLoopGroup
- OioServerSocketChannel 替换 NioServerSocketChannel
很大程度上面得到了复用,并且代码也不在那么的复杂。
传输API
传输API的核心就是 channel 接口了,他被用于所有的IO操作,结构图:
如上图所示,没一个Channel 都对应一个ChannelPipeline 和 ChannelConfig ,
ChannelConfig 包含了该Channel的所有配置设置,并且支持热更新。
由于Channel 是独一无二的,为了保证顺序性,该接口继承 Comparable,一旦两个Channel实例有2个相同的实例,那么 public int compareTo(T o); 方法就会抛出一个 error。
方法介绍
方法名 | 描述 |
---|---|
eventLoop | 返回分配给 Channel 的 EventLoop |
pipeline | 返回分配给 Channel 的 ChannelPipeline |
isActive | 如果 Channel 是活动的,则返回 true。活动的意义可能依赖于底层的传输。 |
localAddress | 返回本地的 SokcetAddress |
remoteAddress | 返回远程的 SocketAddress |
write | 将数据写到远程节点这个数据将被传递给 ChannelPipeline并且排队直到它被 冲刷 |
flush | 将之前已写的数据冲刷到底层传输,如一个 Socket |
writeAndFlush | 一个简便的方法,等同于调用 write()并接着调用 flush() |
代码实例放在github上面了,需要的可以自己去上面看 : netty-notes
建议大家都自己去画一画,加深一下印象。
如果这篇文章,有帮助到大家的,请给作者一个一键三连,谢谢。