图解io+BIO+NIO+AIO+Netty+聊天室实战

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值