2020年开年不平凡,自己在家呆着重新学习一下io
io分为bio,nio,aio
其中netty属于nio,aio目前还没有广泛应用,将来有很大的潜力
理解几个概念
同步和异步
阻塞和非阻塞
io多路复用
I/O多路复用,I/O是指网络I/O, 多路指多个TCP连接(即socket或者channel),复用指复用一个或几个线程。
简单来说:就是使用一个或者几个线程处理多个TCP连接 最大优势是减少系统开销小,不必创建过多的进程/线程,也不必维护这些进程/线程
Reactor反应堆模式:基于事件驱动的,常见的有redis,netty等应用
epoll
1)没fd这个限制,所支持的FD上限是操作系统的最大文件句柄数,1G内存大概支持10万个句柄
2)效率提高,使用回调通知而不是轮询的方式,不会随着FD数目的增加效率下降
3)通过callback机制通知,内核和用户空间mmap同一块内存实现
首先图解io的简单交互流程
单机内部io
从流程图可以看出,外部交互首先是socket通过网卡,通过对应的端口进入,之后通过数据准备阶段进入内核空间,内核空间再经过数据拷贝到用户空间,进而应用程序进行处理。其他和涉及到同步和异步,阻塞和非阻塞的理解,这里就不做介绍了。
此图是阻塞io的调用过程
网络io模型
数据读写过程
java.io模型
BIO:jdk1.4之前主要网络io编程的方式,同步阻塞
bio单线程 请求阻塞和读写阻塞,一次只能服务一个客户端
bio多线程 请求阻塞和读写阻塞,一个线程服务一个客户端,可以服务多个客户端
BIO聊天室
TalkServerBIO bio服务端
//聊天室服务端bio实现
public class TalkServerBIO {
//客户端列表
public static List userList = new ArrayList<>();
public static void main(String[] args) throws IOException {
//服务端端口设置
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("美女聊天室等待您的连接......");
while (true){
Socket socket = serverSocket.accept();
System.out.println("尊敬的客户欢迎您的到来");
MyChannel myChannel = new MyChannel(socket);
new Thread(myChannel).start();
userList.add(myChannel);
}
}
}
TalkClientBIO bio客户端实现
//聊天室客户端bio实现
@Slf4j
public class TalkClientBIO {
private static File file = new File("Client.propertise");
private static Properties properties = new Properties();
private static String HOST = "";
private static int PORT =0;
//初始化端口启动信息
static {
if(!file.exists()){
try {
file.createNewFile();
FileOutputStream fos = new FileOutputStream(file);
properties.setProperty("hostname","localhost");
properties.setProperty("port","9999");
properties.store(fos,null);
fos.close();
} catch (IOException e) {
log.error("创建文件失败");
e.printStackTrace();
}
}
try {
properties.load(new FileInputStream(file));
HOST = properties.getProperty("hostname");
PORT = Integer.parseInt(properties.getProperty("port"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try {
Socket socket = new Socket(HOST,PORT);
Scanner input = new Scanner(System.in);
System.out.println("客户端已经启动,请输入您的昵称: ");
String name = input.next();
System.out.println("已经进入聊天室!");
new Thread(new SendUtil(socket),name).start();
new Thread(new ReceiveUtil(socket),name).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyChannel 消息通道的实现
//server服务端的管道通讯实现
@Slf4j
public class MyChannel implements Runnable {
//输入流
private DataInputStream dis;
//输出流
private DataOutputStream dos;
//管道关闭标识
private boolean isStop = Boolean.FALSE;
/**
* 构造函数获取连接相关信息
* @param client
*/
public MyChannel(Socket client){
try {
dis = new DataInputStream(client.getInputStream());
dos = new DataOutputStream(client.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
log.info("管道消息异常{}",client.getInetAddress());
colse();
}
}
@Override
public void run() {
while (!isStop){
sendOther();
}
}
/**
*
* 获取消息
* @return
*/
public String receiveMsg(){
String msg ="";
try {
msg = dis.readUTF();
System.out.println(Thread.currentThread().getName()+":"+msg);
} catch (IOException e) {
colse();
TalkServerBIO.userList.remove(this);
}
return msg;
}
public void sendMsg(String s){
if(!StringUtils.isEmpty(s)){
try {
dos.writeUTF(s);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
isStop = true;
colse();
}
}
}
/**
* 给其他客户端发送消息
*/
public void sendOther(){
String s = this.receiveMsg();
List<MyChannel> userList = TalkServerBIO.userList;
for(MyChannel myChannel:userList){
if(this == myChannel){
continue;
}
//给其他人发送消息
myChannel.sendMsg(s);
}
}
/**
* 关闭连接
*/
public void colse(){
isStop = true;
if(null!=dis){
try {
dis.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
if(null!=dos){
try {
dos.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}
//发送信息工具类
public class SendUtil implements Runnable {
private DataOutputStream dos = null;
private boolean isStop = Boolean.FALSE;
private BufferedReader br = null;
public SendUtil(Socket socket){
br = new BufferedReader(new InputStreamReader(System.in));
try {
dos = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
colse();
}
}
/**
* 发送消息
*/
public v