IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> bio与nio的几种实现方式 -> 正文阅读

[网络协议]bio与nio的几种实现方式

在socket 通讯层面,BIO就是阻塞io,也就是说,在socket等待连接事件或者读写事件的发生的过程中,当前线程会一直处于阻塞状态,不能做其他事情;

1、bio

public static void main(String[] args) throws IOException {
 //bio阻塞io,主线程只能处理一个连接,当前连接没有处理完,是不能接收新链接的,这对于并发来讲,是非常不友好的
 ?  ServerSocket serverSocket = new ServerSocket(9000);
 ?  while(true) {
 ? ? ?  //此处会阻塞,等待客户端连接
 ? ? ?  Socket socket = serverSocket.accept();
 ? ? ?  System.out.println("客户端已连接");
 ? ? ?  byte[] bytes = new byte[128];
 ? ? ?  //此处会阻塞,等待客户端发送数据
 ? ? ?  int len = socket.getInputStream().read(bytes);
 ? ? ?  System.out.println("客户端发送数据:" + new String(bytes, 0, len));
 ?  }
?
}

2、bio-plus

对于上种情形,可以简单地做出改进,也就是利用多线程把建立连接和处理读写事件分开,这样,二者之间就不会互相影响

public static void main(String[] args) throws IOException {
?
 ?  //加强版bio,利用异步线程去做收发数据,主线程只负责建立连接,这样,即时已连接客户端不发送数据,主线程也不会一直阻塞,可以继续循环接收新的连接;
 ?  // 但是,这样在并发高的情况下,对内存是巨大的消耗;于是,nio就应运而生;
 ?  ServerSocket serverSocket = new ServerSocket(9000);
 ?  while(true) {
 ? ? ?  Socket socket = serverSocket.accept();
 ? ? ?  System.out.println("客户端已连接");
 ? ? ?  new Thread(() -> {
 ? ? ? ? ?  byte[] bytes = new byte[128];
 ? ? ? ? ?  int len = 0;
 ? ? ? ? ?  try {
 ? ? ? ? ? ? ?  len = socket.getInputStream().read(bytes);
 ? ? ? ? ?  } catch (IOException e) {
 ? ? ? ? ? ? ?  e.printStackTrace();
 ? ? ? ? ?  }
 ? ? ? ? ?  System.out.println("客户端发送数据:" + new String(bytes, 0, len));
 ? ? ?  }).start();
 ?  }
?
}

bio-plus版虽然将建立连接和处理读写事件分开了,但是在高并发的情况下仍然“不堪一击”,并且,这种方式虽然看似是解决了bio的阻塞问题,但是属于治标不治本,它的解决方式只不过是利用空间(开辟新线程)去换取时间而已,实质上还是阻塞IO;

3.Nio-Select

于是,nio非阻塞式IO出现了,如上所示,将通道对象serverSocketChannel设置为非阻塞即可,并且引入了selector选择器(可以认为就是socketList),将建立好的连接对象放到socketList中,每次循环就会将所有的socket连接遍历一遍,处理读写事件。这样,单个线程也可以处理多个连接和事件。

public class NioSelect {
 ?  private static List<SocketChannel> socketList=new ArrayList();
 ?  public static void main(String[] args) throws IOException {
 ? ? ?  //nio演示,接收客户端连接和接收数据都不会阻塞;
 ? ? ?  ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 ? ? ?  serverSocketChannel.socket().bind(new InetSocketAddress(9000));
 ? ? ?  //设置serverSocketChannel为非阻塞
 ? ? ?  serverSocketChannel.configureBlocking(false);
 ? ? ?  System.out.println("服务启动");
 ? ? ?  while(true) {
 ? ? ? ? ?  SocketChannel socketChannel = serverSocketChannel.accept();
 ? ? ? ? ?  if(socketChannel!=null){
 ? ? ? ? ? ? ?  System.out.println("客户端已连接");
 ? ? ? ? ? ? ?  //设置socketChannel为非阻塞
 ? ? ? ? ? ? ?  socketChannel.configureBlocking(false);
 ? ? ? ? ? ? ?  //将客户端连接放到集合中
 ? ? ? ? ? ? ?  socketList.add(socketChannel);
 ? ? ? ? ?  }
 ? ? ? ? ?  //遍历所有的socket连接,获取数据;但是这样会有空轮训的问题,就是没有发送数据的连接也会被遍历,不合理
 ? ? ? ? ?  while(socketList.size()>0){
 ? ? ? ? ? ? ?  for(int i=0;i<socketList.size();i++){
 ? ? ? ? ? ? ? ? ?  ByteBuffer allocate = ByteBuffer.allocate(128);
 ? ? ? ? ? ? ? ? ?  int read = socketList.get(i).read(allocate);
 ? ? ? ? ? ? ? ? ?  if(read>0){
 ? ? ? ? ? ? ? ? ? ? ?  System.out.println("客户端发送消息:"+new String(allocate.array()));
 ? ? ? ? ? ? ? ? ?  }else{
 ? ? ? ? ? ? ? ? ? ? ?  socketList.remove(i);
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ?  }
 ? ? ?  }
?
?
 ?  }
}

但是,问题仍然存在。比如此时socketList维护了100个连接,只有一个连接向服务端发送了数据,但是,按照代码却需要将着100个连接全部遍历一遍,这显然是不合理的,并且,socketList中可以存放的连接数量也是有限的,无法解决c10k问题。(所谓c10k问题,指的是:服务器如何支持10k个并发连接)

4.Nio-Epoll

public class NioEpoll {
 ?  private static List<SocketChannel> socketList=new ArrayList();
 ?  public static void main(String[] args) throws IOException {
 ? ? ?  //nio演示,接收客户端连接和接收数据都不会阻塞;
 ? ? ?  ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
 ? ? ?  serverSocketChannel.socket().bind(new InetSocketAddress(9000));
 ? ? ?  //设置serverSocketChannel为非阻塞
 ? ? ?  serverSocketChannel.configureBlocking(false);
 ? ? ?  //获取selector选择器
 ? ? ?  Selector selector = Selector.open();
 ? ? ?  //将serverSocketChannel注册到选择器,并且声明需要选择器监听的是accept事件
 ? ? ?  serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
 ? ? ?  System.out.println("服务启动");
 ? ? ?  while(true) {
 ? ? ? ? ?  //选择器开始执行监听,无监听事件发生会一直阻塞,有监听事件发生会跳出阻塞,继续往下执行
 ? ? ? ? ?  selector.select();
 ? ? ? ? ?  Set<SelectionKey> selectionKeys = selector.selectedKeys();
 ? ? ? ? ?  Iterator<SelectionKey> iterator = selectionKeys.iterator();
 ? ? ? ? ?  while(iterator.hasNext()){
 ? ? ? ? ? ? ?  SelectionKey selectionKey = iterator.next();
 ? ? ? ? ? ? ?  //判断发生的是什么事件
 ? ? ? ? ? ? ?  //是接收连接事件,就获取新的客户连接,及事件的注册
 ? ? ? ? ? ? ?  if(selectionKey.isAcceptable()){
 ? ? ? ? ? ? ? ? ?  ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
 ? ? ? ? ? ? ? ? ?  SocketChannel socketChannel = channel.accept();
 ? ? ? ? ? ? ? ? ?  System.out.println("客户端连接成功");
 ? ? ? ? ? ? ? ? ?  socketChannel.configureBlocking(false);
 ? ? ? ? ? ? ? ? ?  //监听的是数据读事件
 ? ? ? ? ? ? ? ? ?  socketChannel.register(selector,SelectionKey.OP_READ);
 ? ? ? ? ? ? ? ? ?  //如果是发生数据读事件,则接收数据,并打印
 ? ? ? ? ? ? ?  }else if(selectionKey.isReadable()){
 ? ? ? ? ? ? ? ? ?  SocketChannel channel = (SocketChannel) selectionKey.channel();
 ? ? ? ? ? ? ? ? ?  ByteBuffer allocate = ByteBuffer.allocate(128);
 ? ? ? ? ? ? ? ? ?  int read = channel.read(allocate);
 ? ? ? ? ? ? ? ? ?  if(read>0){
 ? ? ? ? ? ? ? ? ? ? ?  System.out.println("客户端发送数据:"+new String(allocate.array(),0,read,"GBK"));
 ? ? ? ? ? ? ? ? ?  }else{
 ? ? ? ? ? ? ? ? ? ? ?  System.out.println("客户端已断开连接");
 ? ? ? ? ? ? ? ? ? ? ?  channel.close();
 ? ? ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  }
 ? ? ? ? ? ? ?  //将处理过的事件从列表中移除,防止重复处理
 ? ? ? ? ? ? ?  iterator.remove();
?
 ? ? ? ? ?  }
 ? ? ?  }
 ?  }
}

这个版本的nio看起来就又进步了一节,这里用epoll选择器代替了上面的select选择器,epoll选择器内部可以分成两部分,一部分是注册到其上的对象列表,另一个部分是有事件发生的列表,于是,可以想到的就是,相比于select遍历所有,epoll只需要遍历有事件发生的对象即可。

其实,在select和epoll之间还有一个poll选择器,只不过poll与select相比,仅仅是存放的连接对象数量变多了,其他的并没有什么区别。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-12-08 14:09:52  更:2021-12-08 14:12:11 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年7日历 -2024/7/8 13:13:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码