一、Netty入门

1.传统IO与NIO
NIO即New IO,这个库是在JDK1.4中才引入的。NIO和IO有相同的作用和目的,但实现方式不同,NIO主要用到的是块,所以NIO的效率要比IO高很多。在Java API中提供了两套NIO,一套是针对标准输入输出NIO,另一套就是网络编程NIO。

1.传统IO特点

①服务端阻塞点
server.accept();获取套接字的时候
inputStream.read(bytes);输入流读取数据的时候
②传统socket是短连接,可以做短连接服务器,他无法做长连接,属于一问一答的模式,比如老的tomcat底层用的就是socket,用完就会关掉线程,因此不会出现线程一直被占用的情况,支持处理多个客户端连接
(1)单线程情况下只能有一个客户端(一个线程维护一个连接,也就是一个socket客户连接)线程一直被占用。
(2)用线程池可以有多个客户端连接,但是非常消耗性能(用此案城池,就是老tomcat原理,只不过是用完后就释放)

2.NIO的特点

主要API介绍:
ServerSocketChannel对应传统IO中的ServerSocket。
SocketChannel对应传统IO中的Socket。
Selector 是NIO核心 ,负载监听 ServerSocketChannel与SocketChannel ,支持单线程连多个客户端;类似通道管理器而且底层是c实现的;线程拥有一个selector就可以支持多个客户端。
SelectionKey 相当于map中的key 相当于记录根据不同动作做不同事情,一个key一个事件。

3.NIO和IO的主要区别

下表总结了Java IO和NIO之间的主要区别:


3.1面向流与面向缓冲

Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。 Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓存到一个缓冲区。 Java NIO的缓冲导向方法略有不同。数据读取到一个它稍后处理的缓冲区,需要时可在缓冲区中前后移动。这就增加了处理过程中的灵活性。但是,还需要检查是否该缓冲区中包含所有您需要处理的数据。而且,需确保当更多的数据读入缓冲区时,不要覆盖缓冲区里尚未处理的数据。

3.2阻塞与非阻塞IO

Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞IO的空闲时间用于在其它通道上执行IO操作,所以一个单独的线程现在可以管理多个输入和输出通道(channel)。

3.3选择器(Selectors)

Java NIO的选择器允许一个单独的线程来监视多个输入通道,你可以注册多个通道使用一个选择器,然后使用一个单独的线程来“选择”通道:这些通道里已经有可以处理的输入,或者选择已准备写入的通道。这种选择机制,使得一个单独的线程很容易来管理多个通道。

4.NIO和IO如何影响应用程序的设计

无论您选择IO或NIO工具箱,可能会影响您应用程序设计的以下几个方面:

1.对NIO或IO类的API调用。
2.数据处理。
3.用来处理数据的线程数。

4.1API调用

当然,使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。

4.2数据处理
使用纯粹的NIO设计相较IO设计,数据处理也受到影响。

在IO设计中,我们从InputStream或 Reader逐字节读取数据。假设你正在处理一基于行的文本数据流,例如:

Name: Anna
Age: 25
Email: anna@mailserver.com
Phone: 1234567890

该文本行的流可以这样处理:

InputStream input = ... ; // get the InputStream from the client socket   BufferedReader reader = new BufferedReader(new InputStreamReader(input));   String nameLine   = reader.readLine();
String ageLine    = reader.readLine();
String emailLine  = reader.readLine();
String phoneLine  = reader.readLine();

请注意处理状态由程序执行多久决定。换句话说,一旦reader.readLine()方法返回,你就知道肯定文本行就已读完, readline()阻塞直到整行读完,这就是原因。你也知道此行包含名称;同样,第二个readline()调用返回的时候,你知道这行包含年龄等。 正如你可以看到,该处理程序仅在有新数据读入时运行,并知道每步的数据是什么。一旦正在运行的线程已处理过读入的某些数据,该线程不会再回退数据(大多如此)。下图也说明了这条原则:

而一个NIO的实现会有所不同,下面是一个简单的例子:

ByteBuffer buffer = ByteBuffer.allocate(48);
int bytesRead = inChannel.read(buffer);

注意第二行,从通道读取字节到ByteBuffer。当这个方法调用返回时,你不知道你所需的所有数据是否在缓冲区内。你所知道的是,该缓冲区包含一些字节,这使得处理有点困难。假设第一次 read(buffer)调用后,读入缓冲区的数据只有半行,例如,“Name:An”,你能处理数据吗?显然不能,需要等待,直到整行数据读入缓存,在此之前,对数据的任何处理毫无意义。所以,你怎么知道是否该缓冲区包含足够的数据可以处理呢?好了,你不知道。发现的方法只能查看缓冲区中的数据。其结果是,在你知道所有数据都在缓冲区里之前,你必须检查几次缓冲区的数据。这不仅效率低下,而且可以使程序设计方案杂乱不堪。例如:

ByteBuffer buffer = ByteBuffer.allocate(48);   int bytesRead = inChannel.read(buffer);   while(! bufferFull(bytesRead) ) {   bytesRead = inChannel.read(buffer);
}

bufferFull()方法必须跟踪有多少数据读入缓冲区,并返回真或假,这取决于缓冲区是否已满。换句话说,如果缓冲区准备好被处理,那么表示缓冲区满了。

bufferFull()方法扫描缓冲区,但必须保持在bufferFull()方法被调用之前状态相同。如果没有,下一个读入缓冲区的数据可能无法读到正确的位置。这是不可能的,但却是需要注意的又一问题。

如果缓冲区已满,它可以被处理。如果它不满,并且在你的实际案例中有意义,你或许能处理其中的部分数据。但是许多情况下并非如此。下图展示了“缓冲区数据循环就绪”:

二.基于IO的java网络程序

使用IDEA创建服务端和客户端程序(新建java工程)

服务器端代码:


import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class server {public static void main(String[] args) throws IOException {//创建客户端的Socket对象(SevereSocket)//ServerSocket (int port)创建绑定到指定端口的服务器套接字ServerSocket ss=new ServerSocket(50000);//Socket accept()侦听要连接到此套接字并接受他Socket s=ss.accept();//获取输入流,读数据,并把数据显示在控制台InputStream is=s.getInputStream();byte[] bys=new byte[1024];int len=is.read(bys);String data=new String(bys,0,len);System.out.println("数据是:"+data);//释放资源s.close();ss.close();}}

客户端代码:

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;public class click {public static void main(String[] args) throws IOException{//创建客户端的Socket对象Socket s=new Socket("127.0.0.1", 50000);//获取输出流,写数据OutputStream os=s.getOutputStream();os.write("hello,物联网19级".getBytes());//释放资源s.close();}
}


IO编程模型在客户端较少的情况下运行良好,但是对于客户端比较多的业务来说,单机服务端可能需要支撑成千上万的连接,IO模型可能就不太合适了。IO编程中,我们看到数据读写是以字节流为单位,效率不高。

三.基于NIO的java网络程序

使用IDEA创建服务端和客户端程序(新建java工程)

服务器端代码:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;public class Server {//网络通信IO操作,TCP协议,针对面向流的监听套接字的可选择通道(一般用于服务端)private ServerSocketChannel serverSocketChannel;private Selector selector;/**开启服务端*/public void start(Integer port) throws Exception {serverSocketChannel = ServerSocketChannel.open();selector = Selector.open();//绑定监听端口serverSocketChannel.socket().bind(new InetSocketAddress(port));//设置为非阻塞模式serverSocketChannel.configureBlocking(false);//注册到Selector上serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);startListener();}private void startListener() throws Exception {while (true) {// 如果客户端有请求select的方法返回值将不为零if (selector.select(1000) == 0) {System.out.println("当前没有任务!!!");continue;}// 如果有事件集合中就存在对应通道的keySet<SelectionKey> selectionKeys = selector.selectedKeys();Iterator<SelectionKey> iterator = selectionKeys.iterator();// 遍历所有的key找到其中事件类型为Accept的keywhile (iterator.hasNext()) {SelectionKey key = iterator.next();if (key.isAcceptable())handleConnection();if (key.isReadable())handleMsg(key);iterator.remove();}}}/*** 处理建立连接*/private void handleConnection() throws Exception {SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));}/** 接收信息*/private void handleMsg(SelectionKey key) throws Exception {SocketChannel channel = (SocketChannel) key.channel();ByteBuffer attachment = (ByteBuffer) key.attachment();channel.read(attachment);System.out.println("当前信息: " + new String(attachment.array()));}public static void main(String[] args) throws Exception {Server myServer = new Server();myServer.start(8887);}
}

客户端代码:

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;public class Click {public static void main(String[] args) throws Exception {SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);// 连接服务器if (!socketChannel.connect(new InetSocketAddress("127.0.0.1", 8887))) {while (!socketChannel.finishConnect()) {System.out.println("connecting...");}}//发送数据String str = "hello,物联网19级";ByteBuffer byteBuffer = ByteBuffer.wrap(str.getBytes());socketChannel.write(byteBuffer);System.in.read();}}

端口可能被占用,自行修改

运行效果:

四.基于Netty的java网络程序

使用IDEA创建服务端和客户端程序(新建java工程)

Project Structure…–>Modules–>Dependencies


搜索


勾选Download to

服务器端代码:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;import java.net.InetSocketAddress;/****/
public class Server {private int port;public static void main(String[] args){new Server(12345).start();}public Server(int port) {this.port = port;}public void start() {/*** 创建两个EventLoopGroup,即两个线程池,boss线程池用于接收客户端的连接,* 一个线程监听一个端口,一般只会监听一个端口所以只需一个线程* work池用于处理网络连接数据读写或者后续的业务处理(可指定另外的线程处理业务,* work完成数据读写)*/EventLoopGroup boss = new NioEventLoopGroup(1);EventLoopGroup work = new NioEventLoopGroup();try {/*** 实例化一个服务端启动类,* group()指定线程组* channel()指定用于接收客户端连接的类,对应java.nio.ServerSocketChannel* childHandler()设置编码解码及处理连接的类*/ServerBootstrap server = new ServerBootstrap().group(boss, work).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress(port)).option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast("decoder", new StringDecoder()).addLast("encoder", new StringEncoder()).addLast(new HelloWorldServerHandler());}});//绑定端口ChannelFuture future = server.bind().sync();System.out.println("server started and listen " + port);future.channel().closeFuture().sync();} catch (Exception e) {e.printStackTrace();}finally {boss.shutdownGracefully();work.shutdownGracefully();}}public static class HelloWorldServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("HelloWorldServerHandler active");}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("server channelRead..");System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString());ctx.write("server write"+msg);ctx.flush();}}
}

客户端代码:

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;/****/
public class Click {private static final String HOST = "localhost";private static final int PORT= 12345;public static void main(String[] args){new Click().start(HOST, PORT);}public void start(String host, int port) {EventLoopGroup group = new NioEventLoopGroup();try {Bootstrap client = new Bootstrap().group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast("decoder", new StringDecoder()).addLast("encoder", new StringEncoder()).addLast(new HelloWorldClientHandler());}});ChannelFuture future = client.connect(host, port).sync();future.channel().writeAndFlush("Hello Netty Server ,I am a netty client");future.channel().closeFuture().sync();} catch (Exception e) {e.printStackTrace();} finally {group.shutdownGracefully();}}public static class HelloWorldClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {System.out.println("HelloWorldClientHandler Active");}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println("HelloWorldClientHandler read Message:"+msg);}}
}

运行效果:

五.参考链接

https://blog.csdn.net/gududedabai/article/details/80783499

https://blog.csdn.net/junseven164/article/details/121714791

https://www.cnblogs.com/xiaoxi/p/6576588.html

Java网络编程进化史:从IO到NIO再到Netty相关推荐

  1. 结合代码详细聊聊 Java 网络编程中的 BIO、NIO 和 AIO

    本文从操作系统的角度来解释BIO,NIO,AIO的概念,含义和背后的那些事.本文主要分为3篇. 第一篇 讲解BIO和NIO以及IO多路复用 第二篇 讲解磁盘IO和AIO 第三篇 讲解在这些机制上的一些 ...

  2. NIO详解(一):java网络编程IO总结(BIO、NIO、AIO)

    1.基本概念 在Java网络通信中,最基本的概念就是Socket编程了.Socket又称"套接字" 向网络发出请求或者应答网络请求. Socket 和ServerSocket类库位 ...

  3. 【Java网络编程与IO流】Java之Java Servlet详解

    Java网络编程与IO流目录: [Java网络编程与IO流]Java中IO流分为几种?字符流.字节流.缓冲流.输入流.输出流.节点流.处理流 [Java网络编程与IO流]计算机网络常见面试题高频核心考 ...

  4. 【Java网络编程与IO流】Java中IO流分为几种?字符流、字节流、缓冲流、输入流、输出流、节点流、处理流

    Java网络编程与IO流目录: [Java网络编程与IO流]Java中IO流分为几种?字符流.字节流.缓冲流.输入流.输出流.节点流.处理流 [Java网络编程与IO流]计算机网络常见面试题高频核心考 ...

  5. Java 网络编程系列之 NIO

    Java 网络编程系列之 NIO 第 1 章Java NIO 概述 1.1 IO 概述 IO 的操作方式 1.2 阻塞 IO (BIO) 1.3 非阻塞 IO(NIO) 1.4 异步非阻塞 IO(AI ...

  6. Java网络编程与NIO学习总结

    #Java网络编程与NIO学习总结 这篇总结主要是基于我之前Java网络编程与NIO系列文章而形成的的.主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点.谢谢 #更多详细内容可 ...

  7. Java网络编程(6)NIO - Channel详解

    前言 NIO的三个核心组件:Buffer.Channel.Selector Java网络编程(4)NIO的理解与NIO的三个组件完成了大概的了解 Java网络编程(5)NIO - Buffer详解详细 ...

  8. Java网络编程------IO模型的同步/异步/阻塞/非阻塞(1)

    IO模型的同步/异步/阻塞/非阻塞 一.同步/异步/阻塞/非阻塞 1.同步和异步 2.阻塞和非阻塞 3.同步.异步和阻塞.非阻塞组合 二.IO 1.I/O 2.阻塞IO和非阻塞IO 3.同步IO和同步 ...

  9. Java网络编程和NIO详解开篇:Java网络编程基础

    老曹眼中的网络编程基础 转自:https://mp.weixin.qq.com/s/XXMz5uAFSsPdg38bth2jAA 我们是幸运的,因为我们拥有网络.网络是一个神奇的东西,它改变了你和我的 ...

最新文章

  1. 在express.js上启用HTTPS
  2. 基于catalog 创建RMAN存储脚本
  3. DHTML【2】--HTML
  4. SPRING事务传播特性
  5. MyBatis中动态sql实现时间范围比较的查询
  6. python data analysis | python数据预处理(基于scikit-learn模块)
  7. JavaScript——易班优课YOOC课群在线测试自动答题解决方案(十五)整合升级+引入jQuery
  8. 类方法classmethod 绑定方法
  9. MATLAB安装机器人学工具箱
  10. win7开机动画_仍有4亿中国用户死守win7 你为何还不选择升级?
  11. SpringCloudConfig分布式配置中心-基本使用
  12. Android TextView 使用替换构建出不同样式的字符串
  13. 租房管理系统毕业设计Java_java毕业设计_springboot框架的租房管理系统
  14. Step7 MicroWin V4.0 SP9在Win10系统中的安装方法
  15. word文档保护密码忘 了,怎么取消格式和编辑保护
  16. 计算机房应该配哪种灭火器,机房应选用哪种灭火器?
  17. VSC 最新版本 加入实用功能Pinned
  18. 边缘检测、导数与边缘、什么是梯度
  19. C 程序员必须收藏的资源大全
  20. 一个案例告诉你如何使用 Kyligence + Spark 进行大数据机器学习

热门文章

  1. 年龄到了一定程度才能体会到世界杯的真正意义
  2. MySQL报错原因:ERROR 1292 (22007): Incorrect date value: ‘1988‘ for column ‘birthday‘ at row 1
  3. java jdk 下载镜像,Docker之JDK镜像
  4. 制造业ERP软件对企业物料管理有何益处?
  5. IEEE会议大全及排名
  6. Docker安装和加速器配置
  7. Windows Server 2008 系统加固
  8. UX的设计灵感从哪里来?——看看Megan Wilson的采访
  9. KaiwuDB 分布式数据库-介绍
  10. Html5实现生日快乐网站,自带音效与蛋糕动画!