BIO、NIO、selector、Netty代码Demo示例

文章目录

    • (一)BIO(Blocking I/O 阻塞I/O)
    • (二)NIO(Non-Blocking I/O 非阻塞I/O)
    • (三)IO多路复用--Selector
    • (四)Netty

(一)BIO(Blocking I/O 阻塞I/O)

阻塞I/O的连接accept()方法及数据读取的read()方法都是阻塞的,也就是说没有客户端发起连接时会阻塞,客户端发起连接后不发送数据也会阻塞。

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;/**
* 阻塞IO
*/
public class BioServerDemo {public static void main(String[] args) throws IOException {ServerSocket serverSocket = new ServerSocket(9000);while(true){System.out.println("等待连接。。");//阻塞方法//可以通过控制台输入命令进行连接:telnet localhost 9000,CTRL+]进入Telnet指令Socket clientSocket = serverSocket.accept();System.out.println("有客户端连接了。。");//虽然采用多线程可以支持多个线程同时访问,但是会引发C10K问题//C10K->connection=1w,C10M->connection=1000w,就是连接数很多的意思//new Thread(new Runnable() {//	@Override//	public void run() {//		try {handler(clientSocket);//		} catch (IOException e) {//			e.printStackTrace();//		}//	}//}).start();}}private static void handler(Socket clientSocket) throws IOException {byte[] bytes = new byte[1024];System.out.println("准备read。。");//接收客户端的数据,阻塞方法,客户端没有发送数据,服务端就会没有数据可读时就阻塞int read  = clientSocket.getInputStream().read(bytes);System.out.println("read完毕。。");if (read !=-1){System.out.println("接收客户端的数据:"+new String(bytes,0,read));}//clientSocket.getOutputStream().write("HelloClint".getBytes(StandardCharsets.UTF_8));//clientSocket.getOutputStream().flush();}
}

(二)NIO(Non-Blocking I/O 非阻塞I/O)

非阻塞I/O在客户端连接方法accept()和read()方法中都不会阻塞,我们可以通过返回值判断是否有客户端发起连接或者发送数据,进行相应的处理。
简单的NIO因为是通过遍历的方式,会有大量的空循环

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;/***初版- NIO(非阻塞)编程*/
public class NioServer {static List<SocketChannel> channelList = new ArrayList<>();public static void main(String[] args) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9000));//这里可选非阻塞和阻塞,如果选择阻塞true,下面的accept()方法会阻塞线程serverSocketChannel.configureBlocking(false);System.out.println("服务启动成功");while (true){SocketChannel socketChannel = serverSocketChannel.accept();if(socketChannel!=null){System.out.println("连接成功");//这里可选非阻塞和阻塞,如果选择阻塞true,下面的read()方法会阻塞线程socketChannel.configureBlocking(false);//将所有连接channel交给一个集合进行管理channelList.add(socketChannel);}//问题点: 空循环时间耗时太久Iterator<SocketChannel> iterator = channelList.iterator();//遍历访问所有channel集合获取客户端发送的数据,如果有任何一个连接客户端发送了数据,那么就处理当前channel里的数据while (iterator.hasNext()){SocketChannel sc = iterator.next();ByteBuffer byteBuffer = ByteBuffer.allocate(128);int len = sc.read(byteBuffer);if (len>0){System.out.println("接收到消息:"+new String(byteBuffer.array()));}else if(len ==-1){iterator.remove();System.out.println("客户端断开连接");}}}}
}

(三)IO多路复用–Selector

通过一个多路复用器selector对channel进行管理,这样
在这里插入图片描述

import java.io.IOException;
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;
/***进阶版- NIO(非阻塞)编程* 这是netty和Redis的雏形*/
public class NioSelectorServer {public static void main(String[] args) throws IOException {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress(9000));serverSocketChannel.configureBlocking(false);//启用epoll模型,这个对应epoll_create()方法Selector selector = Selector.open();//注册阻塞事件:创建连接,这个对应epoll模型的epoll_ctl()方法SelectionKey selectionKey = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);System.out.println("服务启动成功");while (true){//阻塞等待需要处理的事件发生,包括连接事件和数据读取事件,如果没有客户端发起连接或者客户端发送数据,这里会一直阻塞。这个对应epoll模型的epoll_wait()方法selector.select();//获取阻塞的事件Set<SelectionKey> selectionKeys = selector.selectedKeys();//对阻塞事件进行遍历Iterator<SelectionKey> iterator = selectionKeys.iterator();if(iterator.hasNext()){SelectionKey key =iterator.next();if(key.isAcceptable()){//这里只针对连接事件,ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel socketChannel = server.accept();socketChannel.configureBlocking(false);//连接建立后,注册阻塞事件:读取数据SelectionKey selKey = socketChannel.register(selector,SelectionKey.OP_READ);}else if(key.isReadable()){//这里只针对read事件,有需要可以针对write事件处理SocketChannel socketChannel = (SocketChannel) key.channel();ByteBuffer byteBuffer = ByteBuffer.allocate(128);socketChannel.configureBlocking(false);int len  = socketChannel.read(byteBuffer);if(len >0 ){System.out.println("接收到消息:"+new String(byteBuffer.array()));}else if(len ==-1 )//关闭socketsocketChannel.close();System.out.println("接收完成");}}//把处理完的阻塞事件移除iterator.remove();}}}
}

(四)Netty

Netty核心组件

  • Bootstrap和ServerBootstrap:当需要连接客户端或者服务器绑定指定端口时需要使用Bootstrap,ServerBootstrap有两种类型,一种是用于客户端的Bootstrap,一种是用于服务端 的ServerBootstrap。
  • Channel:相当于socket,与另一端进行通信的通道,具备bind、connect、read、write等IO操作的能力。
  • EventLoop:事件循环,负责处理Channel的IO事件,一个EventLoopGroup包含多个EventLoop,一个EventLoop可被分配至多个Channel,一个Channel只能注册于一个EventLoop,一个EventLoop只能与一个Thread绑定。
  • ChannelFuture:channel IO事件的异步操作结果。
  • ChannelHandler:包含IO事件具体的业务逻辑。
  • ChannelPipeline:ChannelHandler的管道容器。

DEMO
Netty服务端

public class NettyServer {public static void main(String[] args) throws InterruptedException {EventLoopGroup parentGroup = new NioEventLoopGroup();EventLoopGroup childGroup = new NioEventLoopGroup();try {//2.创建服务端启动引导/辅助类:ServerBootstrapServerBootstrap bootstrap = new ServerBootstrap();//3.给引导类配置两大线程组,确定了线程模型bootstrap.group(parentGroup, childGroup)// (非必备)打印日志.handler(new LoggingHandler(LogLevel.INFO))// 4.指定 IO 模型.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new StringDecoder());pipeline.addLast(new StringEncoder());/***  服务端添加IdleStateHandler心跳检测处理器,添加自定义处理Handler类实现userEventTriggered()方法作为超时事件的逻辑处理.*  IdleStateHandler心跳检测每十五秒进行一次读检测,如果十五秒内ChannelRead()方法未被调用则触发一次userEventTrigger()方法*  服务端为读IDLE*  pipeline.AddLast(new IdleStateHandler(15, 0, 0));//第一个参数为读,第二个为写,第三个为读写全部*/pipeline.addLast(new IdleStateHandler(15, 0, 0, TimeUnit.SECONDS));//5.可以自定义客户端消息的业务处理逻辑pipeline.addLast(new DemoSocketServerHandler());}});//            ChannelFuture future = bootstrap.bind(8888).sync().addListener(new ChannelFutureListener() {
//                @Override
//                public void operationComplete(ChannelFuture channelFuture) throws Exception {
//                    System.out.println("监听端口已经启动");
//                }
//            });ChannelFuture future = bootstrap.bind(8888).sync().addListener( future1 -> {if (future1.isSuccess()){System.out.println("监听端口已经启动!");} else {System.out.println("监听端口还未启动!");}} );System.out.println("服务器已启动。。。");future.channel().closeFuture().sync();} finally {parentGroup.shutdownGracefully();childGroup.shutdownGracefully();}}
}

Netty客户端

public class NettyClient {public static void main(String[] args) throws InterruptedException {NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();try {Bootstrap bootstrap = new Bootstrap();bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelPipeline pipeline = ch.pipeline();pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));pipeline.addLast(new DemoSocketClientHandler());}});ChannelFuture future = bootstrap.connect("localhost", 8888).sync();future.channel().closeFuture().sync();} finally {if(eventLoopGroup != null) {eventLoopGroup.shutdownGracefully();}}}
}

服务端处理handler

public class DemoSocketServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();String ip = inetSocketAddress.getAddress().getHostAddress();int port = inetSocketAddress.getPort();super.channelActive(ctx);System.out.println(ip+":"+port+" 上线了");}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {System.out.println(msg);System.out.println("Client Address ====== " + ctx.channel().remoteAddress());ctx.channel().writeAndFlush("from server:" + UUID.randomUUID());ctx.fireChannelActive();TimeUnit.MILLISECONDS.sleep(500);}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent idleStateEvent = (IdleStateEvent) evt;if (idleStateEvent.state() == IdleState.READER_IDLE) {InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();String ip = inetSocketAddress.getAddress().getHostAddress();System.out.println((ip + ":" + inetSocketAddress.getPort() + "close"));ctx.channel().close();}}}
}

客户端处理handler

public class DemoSocketClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg)throws Exception {System.out.println(msg);ctx.channel().writeAndFlush("from client: " + System.currentTimeMillis());TimeUnit.MILLISECONDS.sleep(5000);}@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {ctx.channel().writeAndFlush("from client:begin talking");}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();ctx.close();}//超时则关闭链路@Overridepublic void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {if (evt instanceof IdleStateEvent) {IdleStateEvent idleStateEvent = (IdleStateEvent) evt;if (idleStateEvent.state() == IdleState.READER_IDLE) {InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();String ip = inetSocketAddress.getAddress().getHostAddress();System.out.println((ip + ":" + inetSocketAddress.getPort() + "close"));ctx.channel().close();}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/182392.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【hacker送书第5期】SQL Server从入门到精通(第5版)

第5期图书推荐 内容简介作者简介图书目录参与方式 内容简介 SQL Server从入门到精通&#xff08;第5版&#xff09;》从初学者角度出发&#xff0c;通过通俗易懂的语言、丰富多彩的实例&#xff0c;详细介绍了SQL Server开发所必需的各方面技术。全书分为4篇共19章&#xff0c;…

C陷阱与缺陷——第5章库函数

1. 返回整数的getchar函数 #include <stdio.h>main() {char c;while((c getchar()) ! EOF){putchar(c);} } 上述函数是错误的&#xff0c;原因在于程序中的变量c被声明为char类型&#xff0c;而不是int类型&#xff0c;这意味着c无法容下所有可能的字符&#xff0c;特…

Java,反射机制与反射的应用

关于反射&#xff1a; 有时对象的编译时类型和运行时类型是不一致的。比如在使用多态的场景下&#xff0c;有一个Object类型的数组&#xff0c;其中的元素有着各种不同的类型&#xff0c;而调用相应的元素的方法时&#xff0c;比如调用toString方法时&#xff0c;希望调用的是…

优化机器学习:解析数据归一化的重要性与应用

在机器学习中&#xff0c;数据归一化是一种数据预处理的技术&#xff0c;旨在将数据转换为相似的范围或标准化的分布。这样做的主要目的是消除不同特征之间的量纲差异或数值范围差异&#xff0c;以确保模型在训练时更稳定、更有效地学习特征之间的关系。 通常&#xff0c;机器…

linux下ffmpeg安装

1.下载安装MP3编码库 因为FFmpeg默认只支持mp3的解码&#xff0c;不支持mp3编码。如果想把提取出来的音频保存为mp3格式肯定就需要mp3格式的编码库。因此&#xff0c;想输出mp3文件&#xff0c;需要借助第三方的mp3编码库。这里采用LAME编码库&#xff0c;即Lame Aint an MP3 E…

智能优化算法应用:基于水循环算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于水循环算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于水循环算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.水循环算法4.实验参数设定5.算法结果6.参考文献7.…

Oracle把多行查询结果合并为一行

SELECT zjxm FROM pb_zjzcy&#xff0c;Oracle数据库&#xff0c;把zjxm合并到一个字段 在Oracle数据库中&#xff0c;你可以使用LISTAGG函数将多个行中的zjxm字段合并到一个字段中。以下是一个示例查询&#xff1a; SELECT LISTAGG(zjxm, ,) WITHIN GROUP (ORDER BY zjxm) A…

安卓apk抓包

起因 手机&#xff08;模拟器&#xff09;有时候抓不到apk的包&#xff0c;需要借助Postern设置一个代理&#xff0c;把模拟器的流量代理到物理机的burp上。 解决方案 使用Postern代理&#xff0c;把apk的流量代理到burp。 Postern是一个用于代理和网络流量路由的工具&#xf…

C++ day44完全背包问题 零钱兑换Ⅱ 组合总和Ⅳ

完全背包&#xff1a;一个物品可以使用无数次&#xff0c;将01背包中倒序遍历背包变成正序遍历背包 遍历顺序&#xff1a;在完全背包中&#xff0c;对于一维dp数组来说&#xff0c;其实两个for循环嵌套顺序是无所谓的&#xff01; 先遍历物品&#xff0c;后遍历背包可以&#…

win10 下 mvn install 报错:编码GBK不可映射字符

问题背景 由于jenkins需要部署不同的项目&#xff0c;需要使用不同的jdk版本&#xff0c;所以需要配置单独的settings.xml&#xff0c;使用指定的jdk版本进行编译&#xff0c;这里需要单独的maven设置&#xff0c;在配置完后进行mvn的install的时候&#xff0c;由于存在中文注释…

Maven——Maven使用基础

1、安装目录分析 1.1、环境变量MAVEN_HOME 环境变量指向Maven的安装目录&#xff0c;如下图所示&#xff1a; 下面看一下该目录的结构和内容&#xff1a; bin&#xff1a;该目录包含了mvn运行的脚本&#xff0c;这些脚本用来配置Java命令&#xff0c;准备好classpath和相关…

在 Nginx 配置中,root 和 alias 指令的区别是什么

疑问root和alias的区别是什么, 如下 location / {alias /Users/lixinyu/MySpace/学成在线-plus/xc-ui-pc-static-portal/;index index.html index.htm;}location / {root /Users/lixinyu/MySpace/学成在线-plus/xc-ui-pc-static-portal/;index index.html index.htm;}解释…

Cytoscape软件下载、安装、插件学习[基础教程]

写在前面 今天分享的内容是自己遇到问题后&#xff0c;咨询社群里面的同学&#xff0c;帮忙解决的总结。 关于Cytoscape&#xff0c;对于做组学或生物信息学的同学基本是陌生的&#xff0c;可能有的同学用这个软件作图是非常溜的&#xff0c;做出来的网络图也是十分的好看&am…

【海思SS528 | VDEC】MPP媒体处理软件V5.0 | VDEC的使用总结

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

距离“全自动”漏洞挖掘又近了一步!腾讯安全大数据实验室论文入选ACM CCS 2023

计算机领域国际权威学术顶会ACM CCS 2023于11月26日在丹麦哥本哈根开幕。腾讯安全大数据实验室团队论文《Hopper: Interpretative Fuzzing for Libraries》被大会收录&#xff0c;昨天&#xff0c;实验室研究员谢雨轩受邀出席大会进行主题分享。 该论文提出了解释性模糊测试&a…

构建私有Registry

当镜像的数量越来越多&#xff0c;镜像管理就变得非常有必要了。有时候我们需要设置一个本地的私人仓库来进行镜像的管理。 本关任务是学习创建和操作一个私人仓库&#xff0c;要求学习者参照示例完成“创建一个私人仓库&#xff0c;将busybox镜像推送到私人仓库&#xff0c;最…

UCSC基因组浏览器用法

UCSC基因组浏览器用法 UCSC基因组浏览器是一个强大的在线工具&#xff0c;主要用于查看和分析多种生物的基因组数据。这个浏览器最初是由加利福尼亚大学圣克鲁兹分校的生物信息学家和计算生物学家开发的&#xff0c;旨在为科研人员提供一个易于访问和使用的界面&#xff0c;用于…

Java数据结构之《合并线性表》问题

一、前言&#xff1a; 这是怀化学院的&#xff1a;Java数据结构中的一道难度中等偏下的一道编程题(此方法为博主自己研究&#xff0c;问题基本解决&#xff0c;若有bug欢迎下方评论提出意见&#xff0c;我会第一时间改进代码&#xff0c;谢谢&#xff01;) 后面其他编程题只要我…

DELETE 请求,如何通过ajax进行发送

基本的 DELETE 请求概念 DELETE 请求用于向服务器发送删除资源的请求。它是 RESTful API 中的一个重要方法&#xff0c;用于删除指定的资源。 在 Axios 中&#xff0c;发送 DELETE 请求需要指定目标 URL&#xff0c;并可选地传递一些参数&#xff0c;例如请求头、请求体等。DE…

spring和springBoot

Spring和Spring Boot小结 Spring和Spring Boot基于IOC AOP理念实现&#xff0c;Spring Boot集成了Spring。Spring框架&#xff1a; Spring框架解决了企业级的开发的复杂性&#xff0c;它是一个容器框架&#xff0c;用于装java对象&#xff08;Bean&#xff09;&#xff0c;使程…