什么是IO
Java中I/O是以流为基础进行数据的输入输出的,所有数据被串行化(所谓串行化就是数据要按顺序进行输入输出)写入输出流。简单来说就是java通过io流方式和外部设备进行交互。
在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上的数据传输流,字符串流,对象流等等等。
比如程序从服务器上下载图片,就是通过流的方式从网络上以流的方式到程序中,在到硬盘中
在了解不同的IO之前先了解:同步与异步,阻塞与非阻塞的区别
什么是BIO
什么是NIO
什么是AIO
AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由操作系统先完成了再通知服务器应用去启动线程进行处理,AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用操作系统参与并发操作,编程比较复杂,JDK1.7之后开始支持。.
AIO属于NIO包中的类实现,其实IO主要分为BIO和NIO,AIO只是附加品,解决IO不能异步的实现
在以前很少有Linux系统支持AIO,Windows的IOCP就是该AIO模型。但是现在的服务器一般都是支持AIO操作
什么Netty
Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
Netty是由NIO演进而来,使用过NIO编程的用户就知道NIO编程非常繁重,Netty是能够能跟好的使用NIO
BIO和NIO、AIO的区别
IO流的分类
按照读写的单位大小来分:
字符流
:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。 (Java代码接收数据为一般为char数组,也可以是别的
)byte数组
)按照实际IO操作来分:
按照读写时是否直接与硬盘,内存等节点连接分:
什么是内核空间
五种IO模型
1.阻塞BIO(blocking I/O)
A拿着一支鱼竿在河边钓鱼,并且一直在鱼竿前等,在等的时候不做其他的事情,十分专心。只有鱼上钩的时,才结束掉等的动作,把鱼钓上来。
在内核将数据准备好之前,系统调用会一直等待所有的套接字,默认的是阻塞方式。
2.非阻塞NIO(noblocking I/O)
3.异步AIO(asynchronous I/O)
4.信号驱动IO(signal blocking I/O)
5.IO多路转接(I/O multiplexing)
H同样也在河边钓鱼,但是H生活水平比较好,H拿了很多的鱼竿,一次性有很多鱼竿在等,H不断的查看每个鱼竿是否有鱼上钩。增加了效率,减少了等待的时间。
IO多路转接是多了一个select函数,select函数有一个参数是文件描述符集合,对这些文件描述符进行循环监听,当某个文件描述符就绪时,就对这个文件描述符进行处理。
IO多路转接是属于阻塞IO,但可以对多个文件描述符进行阻塞监听,所以效率较阻塞IO的高。
什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别
取值(-128-127)
(0-65535)
什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作
在实现序列化接口是时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用
怎么生成SerialversionUID
可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID
两种显示的生成方式(当你一个类实现了Serializable接口,如果没有显示的定义serialVersionUID,Eclipse会提供这个提示功能告诉你去定义 。在Eclipse中点击类中warning的图标一下,Eclipse就会自动给定两种生成的方式。
BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法
Java中流类的超类主要有那些?
超类代表顶端的父类(都是抽象类)
java.io.InputStream
java.io.OutputStream
java.io.Reader
java.io.Writer
为什么图片、视频、音乐、文件等 都是要字节流来读取
IO的常用类和方法,以及如何使用
注意,如果懂IO的普通文件读写操作可以直接点击此处跳过,直接看网络操作IO编程,那个才是重点,点击即会跳转
前面讲了那么多废话,现在我们开始进入主题,后面很长,从开始的文件操作到后面的网络IO操作都会有例子:
注意,如果懂IO的普通文件读写操作可以直接点击此处跳过,直接看网络操作IO编程,那个才是重点,点击即会跳转
IO基本操作讲解
这里的基本操作就是普通的读取操作,如果想要跟深入的了解不同的IO开发场景必须先了解IO的基本操作
1 按字符
流读取文件
1.1 按字符流的·节点流方式读取
注释:
字符流
:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。 (Java代码接收数据为一般为char数组,也可以是别的
)
字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据,图片、文件、音乐视频等。 (Java代码接收数据只能为byte数组
)
FileReader 类:(字符输入流) 注意:new FileReader("D:\test.txt");//文件必须存在
- package com.test.io;
-
- import java.io.FileReader;
- import java.io.IOException;
-
- public class TestFileReader {
- public static void main(String[] args) throws IOException {
- int num=0;
- //字符流接收使用的char数组
- char[] buf=new char[1024];
- //字符流、节点流打开文件类
- FileReader fr = new FileReader("D:\\test.txt");//文件必须存在
- //FileReader.read():取出字符存到buf数组中,如果读取为-1代表为空即结束读取。
- //FileReader.read():读取的是一个字符,但是java虚拟机会自动将char类型数据转换为int数据,
- //如果你读取的是字符A,java虚拟机会自动将其转换成97,如果你想看到字符可以在返回的字符数前加(char)强制转换如
- while((num=fr.read(buf))!=-1) { }
- //检测一下是否取到相应的数据
- for(int i=0;i<buf.length;i++) {
- System.out.print(buf[i]);
- }
- }
- }
1.2 按字符流的·处理流方式读取
效果是一样,但是给了我们有不同的选择操作。进行了一个小封装,加缓冲功能,避免频繁读写硬盘。我这只是简单演示,处理流其实还有很多操作
BufferedReader 类: 字符输入流使用的类,加缓冲功能,避免频繁读写硬盘
- package com.test.io;
-
- import java.io.BufferedReader;
- import java.io.FileReader;
- import java.io.IOException;
-
- public class TestBufferedReader {
- public static void main(String[] args) throws IOException {
- int num=0;
- //字符流接收使用的String数组
- String[] bufstring=new String[1024];
- //字符流、节点流打开文件类
- FileReader fr = new FileReader("D:\\test.txt");//文件必须存在
- //字符流、处理流读取文件类
- BufferedReader br = new BufferedReader(fr);
- //临时接收数据使用的变量
- String line=null;
- //BufferedReader.readLine():单行读取,读取为空返回null
- while((line=br.readLine())!=null) {
- bufstring[num]=line;
- num++;
- }
- br.close();//关闭文件
- for(int i=0;i<num;i++) {
- System.out.println(bufstring[i]);
- }
- }
- }
2 按字符
流写出文件
2.1 按字符流的·节点流方式写出
写出字符,使用(字符流)这种方法写出文件比较适合。比如:输出内容添加到test.txt文件
FileWriter类:(字符输出流),如果写出文件不存在会自动创建一个相对应的文件。使用FileWriter写出文件默认是覆盖原文件,如果要想在源文件添加内容不覆盖的话,需要构造参数添加true参数:看示例了解
- package com.test.io;
-
- import java.io.File;
- import java.io.FileWriter;
- import java.io.IOException;
-
- public class TestFileWriter {
- public static void main(String[] args) throws IOException {
- //File是操作文件类
- File file = new File("D:\\test.txt");//文件必须存在
- //字符流、节点流写出文件类
- //new FileWriter(file,true),这个true代表追加,不写就代表覆盖文件
- FileWriter out=new FileWriter(file,true);
- //写入的字节,\n代表换行
- String str="\nholler";
- //写入
- out.write(str);
- out.close();
- }
- }
2.2 按字符流的·处理流方式写出
- package com.test.io;
-
- import java.io.*;
-
- public class TestBufferedWriter {
- public static void main(String[] args) throws IOException {
- //File是操作文件类
- File file = new File("D:\\test.txt");//文件必须存在
- //字符流、节点流写出文件类
- //new FileWriter(file),这个我没加true代表覆盖文件
- Writer writer = new FileWriter(file);
- 字符流、处理流写出文件类
- BufferedWriter bw = new BufferedWriter(writer);
- bw.write("\n小心");
- bw.close();
- writer.close();
- }
- }
3 按字节
流写入写出文件
3.1 按字节流的·节点流写入写出文件
注释:
字符流
:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。 (Java代码接收数据为一般为char数组,也可以是别的
)
字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据,图片、文件、音乐视频等。 (Java代码接收数据只能为byte数组
)
FileInputStream:(字节输入流)
FileOutputStream:(字节输出流)
- package com.test.io;
-
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- public class TestFileOutputStream {
- public static void main(String[] args) throws IOException {
- //创建字节输入流、节点流方式读取文件
- FileInputStream fis = new FileInputStream("D:\\Akie秋绘 - Lemon(Cover:米津玄師).mp3");
- //创建字节输入流、节点流方式输出文件
- FileOutputStream fos = new FileOutputStream("D:\\copy.mp3");
-
- //根据文件大小做一个字节数组
- byte[] arr = new byte[fis.available()];
- //将文件上的所有字节读取到数组中
- fis.read(arr);
- //将数组中的所有字节一次写到了文件上
- fos.write(arr);
- fis.close();
- fos.close();
- }
- }
3.2 按字节流的·处理流写入写出文件
- package com.test.io;
-
- import java.io.*;
-
- public class TestBufferedOutputStream {
- //创建文件输入流对象,关联致青春.mp3
- public static void main(String[] args) throws IOException {
- FileInputStream fis = new FileInputStream("D:\\copy.mp3");
- //创建缓冲区对fis装饰
- BufferedInputStream bis = new BufferedInputStream(fis);
- //创建输出流对象,关联copy.mp3
- FileOutputStream fos = new FileOutputStream("D:\\copy2.mp3");
- //创建缓冲区对fos装饰
- BufferedOutputStream bos = new BufferedOutputStream(fos);
- //循环直接输出
- int i;
- while((i = bis.read()) != -1) {
- bos.write(i);
- }
- bis.close();
- bos.close();
- }
- }
网络操作IO讲解
我这使用Socket简单的来模拟网络编程IO会带来的问题
不懂Socket可以看我之前的文章,这个东西很容易懂的,就是基于TCP实现的网络通信,比http要快,很多实现网络通信的框架都是基于Socket来实现
网络操作IO编程演变历史
1 BIO编程会出现什么问题?
BIO是阻塞的
例子: 阻塞IO(blocking I/O) A拿着一支鱼竿在河边钓鱼,并且一直在鱼竿前等,在等的时候不做其他的事情,十分专心。只有鱼上钩的时,才结束掉等的动作,把鱼钓上来。
看起来没问题,但是我很多请求一起发送请求资源怎么办:
那不是要等待第一个人资源完成后后面的人才可以继续? 因为BIO是阻塞的所以读取写出操作都是非常浪费资源的
BIO代码示例:(后面有代码,往后移动一点点,认真看,代码学习量很足
)
后面有代码,我这是教运行顺序
) 先不要在控制台 输入 ,模拟堵塞。(我的代码输入了就代表请求完成了)
·发现服务端没效果
,而客户端连接成功(在堵塞当中) 我这启动了俩个Client,注意看,(这俩个代码是一样的)
·BIO通信代码:
- package com.test.io;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
-
- //TCP协议Socket使用BIO进行通信:服务端
- public class BIOServer {
- // 在main线程中执行下面这些代码
- public static void main(String[] args) {
- //使用Socket进行网络通信
- ServerSocket server = null;
- Socket socket = null;
- //基于字节流
- InputStream in = null;
- OutputStream out = null;
- try {
- server = new ServerSocket(8000);
- System.out.println("服务端启动成功,监听端口为8000,等待客户端连接...");
- while (true){
- socket = server.accept(); //等待客户端连接
- System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress());
- in = socket.getInputStream();
- byte[] buffer = new byte[1024];
- int len = 0;
- //读取客户端的数据
- while ((len = in.read(buffer)) > 0) {
- System.out.println(new String(buffer, 0, len));
- }
- //向客户端写数据
- out = socket.getOutputStream();
- out.write("hello!".getBytes());
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- package com.test.io;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.Socket;
- import java.util.Scanner;
-
- //TCP协议Socket使用BIO进行通信:客户端
- public class Client01 {
- public static void main(String[] args) throws IOException {
- //创建套接字对象socket并封装ip与port
- Socket socket = new Socket("127.0.0.1", 8000);
- //根据创建的socket对象获得一个输出流
- //基于字节流
- OutputStream outputStream = socket.getOutputStream();
- //控制台输入以IO的形式发送到服务器
- System.out.println("TCP连接成功 \n请输入:");
- String str = new Scanner(System.in).nextLine();
- byte[] car = str.getBytes();
- outputStream.write(car);
- System.out.println("TCP协议的Socket发送成功");
- //刷新缓冲区
- outputStream.flush();
- //关闭连接
- socket.close();
- }
- }
- package com.test.io;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.Socket;
- import java.util.Scanner;
-
- //TCP协议Socket:客户端
- public class Client02 {
- public static void main(String[] args) throws IOException {
- //创建套接字对象socket并封装ip与port
- Socket socket = new Socket("127.0.0.1", 8000);
- //根据创建的socket对象获得一个输出流
- //基于字节流
- OutputStream outputStream = socket.getOutputStream();
- //控制台输入以IO的形式发送到服务器
- System.out.println("TCP连接成功 \n请输入:");
- String str = new Scanner(System.in).nextLine();
- byte[] car = str.getBytes();
- outputStream.write(car);
- System.out.println("TCP协议的Socket发送成功");
- //刷新缓冲区
- outputStream.flush();
- //关闭连接
- socket.close();
- }
- }
为了解决堵塞问题,可以使用多线程,请看下面
2 多线程解决BIO编程会出现的问题
这时有人就会说,我多线程不就解决了吗?
万一请求越来越多,线程越来越多那我CPU不就炸了?
多线程BIO代码示例:
先启动服务端,在启动所有客户端,测试
,发现连接成功(后面有代码
) 在所有客户端输入消息(Client01、Client02这些是我在客户端输入的消息
):发现没有问题多线程BIO通信代码:
服务端的代码,客户端的代码还是上面之前的代码
- package com.test.io;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
-
- //TCP协议Socket使用多线程BIO进行通行:服务端
- public class BIOThreadService {
- public static void main(String[] args) {
- try {
- ServerSocket server = new ServerSocket(8000);
- System.out.println("服务端启动成功,监听端口为8000,等待客户端连接... ");
- while (true) {
- Socket socket = server.accept();//等待客户连接
- System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress());
- //针对每个连接创建一个线程, 去处理I0操作
- //创建多线程创建开始
- Thread thread = new Thread(new Runnable() {
- public void run() {
- try {
- InputStream in = socket.getInputStream();
- byte[] buffer = new byte[1024];
- int len = 0;
- //读取客户端的数据
- while ((len = in.read(buffer)) > 0) {
- System.out.println(new String(buffer, 0, len));
- }
- //向客户端写数据
- OutputStream out = socket.getOutputStream();
- out.write("hello".getBytes());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- });
- thread.start();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
为了解决线程太多,这时又来了,线程池
3 线程池解决多线程BIO编程会出现的问题
这时有人就会说,我TM用线程池?
线程池固然可以解决这个问题,万一需求量还不够还要扩大线程池。当是这是我们自己靠着自己的思想完成的IO操作,Socket 上来了就去创建线程去抢夺CPU资源,MD,线程都TM做IO去了,CPU也不舒服呀
这时呢:Jdk官方坐不住了,兄弟BIO的问题交给我,我来给你解决:NIO的诞生
线程池BIO代码示例:
先启动服务端,在启动所有客户端,测试
,(后面有代码
) 在所有客户端输入消息(Client01、Client02这些是我在客户端输入的消息
):发现没有问题线程池BIO通信代码:
服务端的代码,客户端的代码还是上面的代码
- package com.test.io;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
-
- //TCP协议Socket使用线程池BIO进行通行:服务端
- public class BIOThreadPoolService {
- public static void main(String[] args) {
- //创建线程池
- ExecutorService executorService = Executors.newFixedThreadPool(30);
- try {
- ServerSocket server = new ServerSocket(8000);
- System.out.println("服务端启动成功,监听端口为8000,等待客户端连接...");
- while (true) {
- Socket socket = server.accept();//等待客户连接
- System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress());
- //使用线程池中的线程去执行每个对应的任务
- executorService.execute(new Thread(new Runnable() {
- public void run() {
- try {
- InputStream in = socket.getInputStream();
- byte[] buffer = new byte[1024];
- int len = 0;
- //读取客户端的数据
- while ((len = in.read(buffer)) > 0) {
- System.out.println(new String(buffer, 0, len));
- }
- //向客户端写数据
- OutputStream out = socket.getOutputStream();
- out.write("hello".getBytes());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- })
- );
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
4 使用NIO实现网络通信
客户端发送的连接请求都会注册到多路复用器上
,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4之后开始支持。
看不懂介绍可以认真看看代码实例,其实不难
什么是通道(Channel)
Channel是一个对象,可以通过它读取和写入数据。 通常我们都是将数据写入包含一个或者多个字节的缓冲区,然后再将缓存区的数据写入到通道中,将数据从通道读入缓冲区,再从缓冲区获取数据。
Channel 类似于原I/O中的流(Stream),但有所区别:
什么是选择器(Selector)
判断是否做完某个操作,完成某个操作后改变不一样的状态
)状态是否发生变化,知道IO操作完成后在退出死循环什么是Buffer(缓冲区)
Buffer 是一个缓冲数据的对象, 它包含一些要写入或者刚读出的数据。
在普通的面向流的 I/O 中,一般将数据直接写入或直接读到 Stream 对象中。当是有了Buffer(缓冲区)后,数据第一步到达的是Buffer(缓冲区)中
缓冲区实质上是一个数组(底层完全是数组实现的,感兴趣可以去看一下
)。通常它是一个字节数组,内部维护几个状态变量,可以实现在同一块缓冲区上反复读写(不用清空数据再写)。
代码实例:
我这客户端和服务端代码有些修该变,后面有代码
服务端示例,先运行,想要搞定NIO请认真看代码示例,真的很清楚
- package com.test.io;
-
- import com.lijie.iob.RequestHandler;
-
- 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;
-
- public class NIOServer {
- public static void main(String[] args) throws IOException {
- //111111111
- //Service端的Channel,监听端口的
- ServerSocketChannel serverChannel = ServerSocketChannel.open();
- //设置为非阻塞
- serverChannel.configureBlocking(false);
- //nio的api规定这样赋值端口
- serverChannel.bind(new InetSocketAddress(8000));
- //显示Channel是否已经启动成功,包括绑定在哪个地址上
- System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."+ serverChannel.getLocalAddress());
-
- //22222222
- //声明selector选择器
- Selector selector = Selector.open();
- //这句话的含义,是把selector注册到Channel上面,
- //每个客户端来了之后,就把客户端注册到Selector选择器上,默认状态是Accepted
- serverChannel.register(selector, SelectionKey.OP_ACCEPT);
-
- //33333333
- //创建buffer缓冲区,声明大小是1024,底层使用数组来实现的
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- RequestHandler requestHandler = new RequestHandler();
-
- //444444444
- //轮询,服务端不断轮询,等待客户端的连接
- //如果有客户端轮询上来就取出对应的Channel,没有就一直轮询
- while (true) {
- int select = selector.select();
- if (select == 0) {
- continue;
- }
- //有可能有很多,使用Set保存Channel
- Set<SelectionKey> selectionKeys = selector.selectedKeys();
- Iterator<SelectionKey> iterator = selectionKeys.iterator();
- while (iterator.hasNext()) {
- //使用SelectionKey来获取连接了客户端和服务端的Channel
- SelectionKey key = iterator.next();
- //判断SelectionKey中的Channel状态如何,如果是OP_ACCEPT就进入
- if (key.isAcceptable()) {
- //从判断SelectionKey中取出Channel
- ServerSocketChannel channel = (ServerSocketChannel) key.channel();
- //拿到对应客户端的Channel
- SocketChannel clientChannel = channel.accept();
- //把客户端的Channel打印出来
- System.out.println("客户端通道信息打印:" + clientChannel.getRemoteAddress());
- //设置客户端的Channel设置为非阻塞
- clientChannel.configureBlocking(false);
- //操作完了改变SelectionKey中的Channel的状态OP_READ
- clientChannel.register(selector, SelectionKey.OP_READ);
- }
- //到此轮训到的时候,发现状态是read,开始进行数据交互
- if (key.isReadable()) {
- //以buffer作为数据桥梁
- SocketChannel channel = (SocketChannel) key.channel();
- //数据要想读要先写,必须先读取到buffer里面进行操作
- channel.read(buffer);
- //进行读取
- String request = new String(buffer.array()).trim();
- buffer.clear();
- //进行打印buffer中的数据
- System.out.println(String.format("客户端发来的消息: %s : %s", channel.getRemoteAddress(), request));
- //要返回数据的话也要先返回buffer里面进行返回
- String response = requestHandler.handle(request);
- //然后返回出去
- channel.write(ByteBuffer.wrap(response.getBytes()));
- }
- iterator.remove();
- }
- }
- }
- }
我这用的不是之前的了,有修改
)运行起来客户端控制台输入消息就好了。 要模拟测试,请复制粘贴改一下,修改客户端的类名就行了,四个客户端代码一样的
, - package com.test.io;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.Socket;
- import java.util.Scanner;
-
- //TCP协议Socket:客户端
- public class Client01 {
- public static void main(String[] args) throws IOException {
- //创建套接字对象socket并封装ip与port
- Socket socket = new Socket("127.0.0.1", 8000);
- //根据创建的socket对象获得一个输出流
- OutputStream outputStream = socket.getOutputStream();
- //控制台输入以IO的形式发送到服务器
- System.out.println("TCP连接成功 \n请输入:");
- while(true){
- byte[] car = new Scanner(System.in).nextLine().getBytes();
- outputStream.write(car);
- System.out.println("TCP协议的Socket发送成功");
- //刷新缓冲区
- outputStream.flush();
- }
- }
- }
5 使用Netty实现网络通信
Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的Socket服务开发。
Netty是由NIO演进而来,使用过NIO编程的用户就知道NIO编程非常繁重,Netty是能够能跟好的使用NIO
Netty的原里就是NIO,他是基于NIO的一个完美的封装,并且优化了NIO,使用他非常方便,简单快捷
我直接上代码:
1、先添加依赖:
- <dependency>
- <groupId>io.netty</groupId>
- <artifactId>netty-all</artifactId>
- <version>4.1.16.Final</version>
- </dependency>
其实只需要添加一行消息就好了
请认真看中间的代码
- package com.lijie.iob;
-
- 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.serialization.ClassResolvers;
- import io.netty.handler.codec.serialization.ObjectEncoder;
- import io.netty.handler.codec.string.StringDecoder;
-
- public class NettyServer {
- public static void main(String[] args) throws InterruptedException {
- EventLoopGroup bossGroup = new NioEventLoopGroup();
- EventLoopGroup workerGroup = new NioEventLoopGroup();
- try {
- ServerBootstrap b = new ServerBootstrap();
- b.group(bossGroup, 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 io.netty.handler.codec.serialization.ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
-
- //重点,其他的都是复用的
- //这是真正的I0的业务代码,把他封装成一个个的个Hand1e类就行了
- //把他当成 SpringMVC的Controller
- pipeline.addLast(new NettyServerHandler());
-
-
- }
- })
- .option(ChannelOption.SO_BACKLOG, 128)
- .childOption(ChannelOption.SO_KEEPALIVE, true);
- ChannelFuture f = b.bind(8000).sync();
- System.out.println("服务端启动成功,端口号为:" + 8000);
- f.channel().closeFuture().sync();
- } finally {
- workerGroup.shutdownGracefully();
- bossGroup.shutdownGracefully();
- }
- }
- }
- package com.lijie.iob;
-
- import io.netty.channel.Channel;
- import io.netty.channel.ChannelHandlerContext;
- import io.netty.channel.ChannelInboundHandlerAdapter;
-
- public class NettyServerHandler extends ChannelInboundHandlerAdapter {
- RequestHandler requestHandler = new RequestHandler();
-
- @Override
- public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
- Channel channel = ctx.channel();
- System.out.println(String.format("客户端信息: %s", channel.remoteAddress()));
- }
-
- @Override
- public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
- Channel channel = ctx.channel();
- String request = (String) msg;
- System.out.println(String.format("客户端发送的消息 %s : %s", channel.remoteAddress(), request));
- String response = requestHandler.handle(request);
- ctx.write(response);
- ctx.flush();
- }
- }
- package com.test.io;
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.Socket;
- import java.util.Scanner;
-
- //TCP协议Socket:客户端
- public class Client01 {
- public static void main(String[] args) throws IOException {
- //创建套接字对象socket并封装ip与port
- Socket socket = new Socket("127.0.0.1", 8000);
- //根据创建的socket对象获得一个输出流
- OutputStream outputStream = socket.getOutputStream();
- //控制台输入以IO的形式发送到服务器
- System.out.println("TCP连接成功 \n请输入:");
- while(true){
- byte[] car = new Scanner(System.in).nextLine().getBytes();
- outputStream.write(car);
- System.out.println("TCP协议的Socket发送成功");
- //刷新缓冲区
- outputStream.flush();
- }
- }
- }
感谢阅读,更多的java课程学习路线,笔记,面试等架构资料,需要的同学可以私信我(面试)即可免费获取!