IO
1.IO模型
1.1 BIO
同步并阻塞。服务器实现一个连接一个线程,客户端在有连接请求时服务器启动一个线程处理,没处理完不能做其他操作。
适用于连接数目小且固定的架构。
- 面向流,只能单向读写,没有数据时会挂起线程阻塞等待,需要对每个连接新建线程处理
- BIOServer
public class BIOServer {
/**
* Server启动 client01启动 client02启动(被阻塞,01输入之后开始连接)
* @param args
*/
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
try {
serverSocket = new ServerSocket(8000);
System.out.println("8000");
while (true){
//等待客户端连接
socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress());
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//读取客户端数据
while ((len = inputStream.read(bytes)) >0 ){
System.out.println(new String(bytes, 0, len));
}
//向客户端写数据
outputStream = socket.getOutputStream();
outputStream.write("Hello BIO".getBytes(StandardCharsets.UTF_8));
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 多线程启动
*/
public void BIOThreadServer(){
try {
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("8000");
while (true){
//等待客户端连接
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress());
//多线程
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//读取客户端数据
while ((len = inputStream.read(bytes)) >0 ){
System.out.println(new String(bytes, 0, len));
}
//向客户端写数据
outputStream = socket.getOutputStream();
outputStream.write("Hello BIO".getBytes(StandardCharsets.UTF_8));
} catch (Exception e){
e.printStackTrace();
}
}
});
thread.start();
}
} catch (Exception e){
e.printStackTrace();
}
}
/**
* 线程池
*/
public void BIOThreadPool(){
//线程池
ExecutorService executorService = Executors.newFixedThreadPool(30);
try {
ServerSocket serverSocket = new ServerSocket(8000);
System.out.println("8000");
while (true){
//等待客户端连接
Socket socket = serverSocket.accept();
System.out.println(socket.getRemoteSocketAddress());
//多线程
executorService.execute( new Thread(new Runnable() {
@Override
public void run() {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = 0;
//读取客户端数据
while ((len = inputStream.read(bytes)) >0 ){
System.out.println(new String(bytes, 0, len));
}
//向客户端写数据
outputStream = socket.getOutputStream();
outputStream.write("Hello BIO".getBytes(StandardCharsets.UTF_8));
} catch (Exception e){
e.printStackTrace();
}
}
}));
}
} catch (Exception e){
e.printStackTrace();
}
}
}
- BIOClient
public class BIOClient01 {
public static void main(String[] args) throws Exception{
Socket socket = new Socket("127.0.0.1",8000);
//字节流
OutputStream outputStream = socket.getOutputStream();
//IO方式发送到服务器
System.out.println("连接成功");
String str = new Scanner(System.in).nextLine();
byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
}
}
1.2 NIO
同步非阻塞。服务器实现一个连接一个线程,客户端发送的请求注册到多路复用器上,轮询到有IO请求才启动线程处理。
适合连接数目多且连接短的架构。
- 面向缓冲,可以双向读写,进行多路复用,一个线程监听多个连接
- 通道:Channel通过它读写数据,数据先写入缓冲区,再将其写入channel,再从channel读到缓冲区
- 选择器:Selector,channel注册到selector中
- 缓冲区:Buffer
- NIOServer
public class NIOServer {
public static void main(String[] args) throws Exception{
//service端的Channel 监听端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//非阻塞
serverSocketChannel.configureBlocking(false);
//赋值端口
serverSocketChannel.bind(new InetSocketAddress(8000));
// 绑定的地址
System.out.println(serverSocketChannel.getLocalAddress());
//声明Selector选择器
Selector selector = Selector.open();
//注册
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
//缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//轮询 客户端连接就取出来channel
while (true) {
int select = selector.select();
if (select == 0){
continue;
}
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()){
//连接客户端和服务端
SelectionKey key = iterator.next();
//判断状态
if (key.isAcceptable()){
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
//客户端对应的Channel
SocketChannel socketChannel = channel.accept();
System.out.println(socketChannel.getRemoteAddress());
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if (key.isReadable()){
SocketChannel channel = (SocketChannel) key.channel();
//想读要先写到buffer
channel.read(byteBuffer);
String request = new String(byteBuffer.array()).trim();
byteBuffer.clear();
System.out.println(request);
String response = request;
channel.write(ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8)));
}
iterator.remove();
}
}
}
}
- NIOClient
public class NIOClient {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("127.0.0.1",8000);
OutputStream outputStream = socket.getOutputStream();
while (true){
byte[] bytes = new Scanner(System.in).nextLine().getBytes(StandardCharsets.UTF_8);
outputStream.write(bytes);
}
}
}
1.3 AIO
异步非阻塞。服务器一个有效请求一个线程,客户端请求由操作系统完成后通知服务器启动线程。
适用于连接数目多且长的架构
1.4 Netty
-
提供异步事件驱动的网络应用程序框架和工具,用以快速开发高性能,高可靠的网络服务器和客户端程序。
-
基于NIO的客户服务器框架,快速开发网络应用
-
NettyServerHandler
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel channel = ctx.channel();
String request = (String) msg;
String response = request;
ctx.write(response);
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
System.out.println(channel.remoteAddress());
}
}
- NettyServer
public class NettyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup boosGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boosGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new StringDecoder());
pipeline.addLast("encoder", new ObjectEncoder());
pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
pipeline.addLast(new NettyServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
} catch (Exception e){
}
}
}
1.5 信号驱动IO
1.6 IO多路转接
2.Java IO流
2.1 字符流:字符为单位,16位数据 char
- 节点流Reader
/**
* 节点流
*/
public void NodeStream() throws Exception{
int num = 0;
//必须存在 read()自动吧char转化为int
FileReader fileReader = new FileReader("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
//第一种
char[] buf = new char[1024];
while ((num = fileReader.read(buf)) != -1) {
System.out.print((new String(buf,0,num)));
}
//第二种
while ((num = fileReader.read()) != -1) {
System.out.print((char)num);
}
}
- 处理流Reader
/**
* 处理流
*/
public void handlerStream() throws Exception{
String[] bufString = new String[1024];
int num = 0;
FileReader fileReader = new FileReader("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = null;
//readLine 单行读取
while ((line = bufferedReader.readLine()) != null){
bufString[num] = line;
num++;
}
bufferedReader.close();
for (int i = 0; i < num; i++){
System.out.println(bufString[i]);
}
}
- 节点流Writer
/**
* 节点流写入
* @throws Exception
*/
public void nodeStreamWriter() throws Exception{
File file = new File("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
//true代表追加 不写就覆盖文件
FileWriter fileWriter = new FileWriter(file, true);
// \n换行
String str = "\nLWX一二三";
fileWriter.write(str);
fileWriter.close();
}
- 处理流Writer
/**
* 处理流Writer
* @throws Exception
*/
public void handlerStreamWriter() throws Exception{
File file = new File("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
//true代表追加 不写就覆盖文件
FileWriter fileWriter = new FileWriter(file, true);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
String str = "\n一二三LWX";
bufferedWriter.write(str);
bufferedWriter.close();
}
2.2 字节流:字节为单位,8位 byte
- 节点流
/**
* 节点流
*/
public void nodeReader() throws Exception {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test1.txt");
byte[] bytes = new byte[fileInputStream.available()];
//读取到数组
fileInputStream.read(bytes);
fileOutputStream.write(bytes);
fileInputStream.close();
fileOutputStream.close();
}
- 处理流
/**
* 处理流
* @throws Exception
*/
public void handlerStream() throws Exception{
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\lwx20\\Desktop\\Java\\Foundation\\IO\\test1.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
//循环输出
int i;
while ((i = bufferedInputStream.read()) != -1){
bufferedOutputStream.write(i);
}
bufferedInputStream.close();
bufferedOutputStream.close();
}
2.3 Java NIO
NIO三个部分:Channel通道 Buffer缓冲区 Selector选择区
传统IO基于字节流和字符流,NIO基于通道和缓冲区操作,数据从通道读到缓冲区,或者从缓冲区读到通道。
选择区用于监听多个通道事件
IO面向流,NIO面向缓冲区
IO面向流表示没有被缓存,不能前后移动数据。NIO缓存区可以前后移动数据。NIO是非阻塞的
2.5.1 Channel
双向的。
主要实现有FileChannel DatagramChannel SocketChannel ServerSocketChannel 对应文件IO UDP tcp
2.5.2 Buffer
读写的数据都必须经过buffer
2.5.3 Selector
检测多个注册的通道上是否有事件发生,可以获取事件并进行处理
一个单线程可以管理多个通道,当有事件发生时,才进行处理,不需要都去创建线程,避免线程上下文开销
3.序列化
- 序列化:二进制形式保存在硬盘上,不保存静态变量
- 反序列化: 二进制文件转化为对象读取
3.1 Serializable接口
- SerialVersionUID: 接口默认生成,默认生成在反序列化时可能会导致InvaildClassException异常(序列化与反序列化比对通过UID进行,反序列化之前修改类,就会报错)
- static final long SerialVersionUID =
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律