io 和nio不得不说的那点破事

0 篇文章 0 订阅

io 和nio 之间的那点事

1 传统的io socket

百度网盘下载地址:链接:https://pan.baidu.com/s/1OpOAk_LqvLc_OpfG-3bUug
提取码:dgy6

在这里插入图片描述

1.1 编码

这个工具通过实测,这个工具发送数据包是使用 GBK的方式进行编码的

我们编写一个传统的Io 测试类:


package com.liu;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by Administrator on 2019/8/28 0028.
 */
public class OioServer {

    public static void main(String[] args) throws IOException {
//
        ExecutorService service = Executors.newCachedThreadPool();//创建连接池
//        创建socket,监听10010
         ServerSocket serverSocket = new ServerSocket(10010);
        System.out.println("服务器启动");

        while (true){
//            获取一个套接字(阻塞)
            Socket socket = serverSocket.accept();// 阻塞消息
            service.execute(new Runnable() {
                @Override
                public void run() {
//                    处理业务
                    handler(socket);
                }
            });

        }





    }

    private static void handler(Socket socket) {


        try {
            byte [] bytes = new byte[1024];
            InputStream inputStream = socket.getInputStream();
            while (true){


                int read = inputStream.read(bytes);
                if(read != -1){

                    System.out.print( Thread.currentThread().getName()+"+++");
                    System.out.println(new String (bytes,0 ,read,Charset.forName("GBK")));
                }else {
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            System.out.println("socket 关闭");
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}


1.2 工具并发测试

可以通过建立多个client进行定时发送任务;

在这里插入图片描述

1.3 并发结果
pool-1-thread-2+++发送
pool-1-thread-1+++范德萨范德萨
pool-1-thread-2+++发送
pool-1-thread-1+++范德萨范德萨
pool-1-thread-2+++发送
pool-1-thread-1+++范德萨范德萨
pool-1-thread-2+++发送
pool-1-thread-1+++范德萨范德萨
pool-1-thread-2+++发送
pool-1-thread-1+++范德萨范德萨
pool-1-thread-2+++发送
pool-1-thread-1+++范德萨范德萨

1.4 结论

通过以上的实例,我们不难看出,我们使用这种传统的io的方式,每当一个客户端连接进来后,我们都需要开销一条线程,去处理这个任务,这样对于我们的系统资源开销很大。

2 nio

2.1 nio 服务类

通过以上的实例,我们可以编制一个nio的server 来测试一下:

package com.liu;

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.nio.charset.Charset;
import java.util.Iterator;

/**
 * Created by Administrator on 2019/8/28 0028.
 */
public class NIOServer {

    // /     通道管理器
    private Selector selector;


    public void  initServer(int port) throws IOException {
//        获得一个serversocket 通道
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//          设置通道为非 阻塞
        serverSocketChannel.configureBlocking(false);
//         通道绑定端口
        serverSocketChannel.socket().bind(new InetSocketAddress(port));
//           通过通道获得一个通道管理器
        this.selector=Selector.open();
//        将通道管理器和该通道绑定,并未该通过到注册SelectKey.op_accept事件,注册该事件后
//        当该事件到达时,selector.select()会返回,如果该事件没有到达就会一直阻塞
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    }

    /**
     * 轮询的方式监听selector 上是否有需要处理的事件,如果有,则进行处理
     * @throws IOException
     */
    public void  listen () throws IOException {
        System.out.println("服务端启动成功!!!");
//      轮询 访问 selector
        while (true){
//            当注册事件到达时,方法返回,否则会一直阻塞。
            selector.select();
//            获得selector中选中项的迭代器,选中项为注册的事件
            Iterator its= this.selector.selectedKeys().iterator();
            while (its.hasNext()){
                SelectionKey key = (SelectionKey) its.next();
//               删除已经选的key,防治重复处理
                its.remove();
                handler(key);
            }

        }
    }

    /**
     *  处理请求
     * @param key
     */
    private void handler(SelectionKey key) throws IOException {
//        客户端请求连接事件
        if (key.isAcceptable()){
            handlerAccept(key);
        }else if (key.isReadable()){
            handlerRead(key);
        }


    }

    /**
     * 处理连接事件
     * @param key
     */
    private void handlerAccept(SelectionKey key) throws IOException {
     ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
//      通过服务 获得客户端连接的通道
     SocketChannel channel = serverChannel.accept();
//    将通道设置成非阻塞
     channel.configureBlocking(false);
     System.out.println("新的客户端连接上来。。。");
//    在和客户端连接成功之后,我们可以接到客户端的信息,需要给通道设置读的权限。
        channel.register(this.selector,SelectionKey.OP_READ);
    }

    /**
     *  处理读的事件
      * @param key
     */
    private void handlerRead(SelectionKey key) throws IOException {
//     服务器可读取的小实习得到事件发生的通道。
        SocketChannel channel = (SocketChannel) key.channel();
//     创建读取的缓冲区
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int read = channel.read(buffer);
        if(read>0){
            byte [] data = buffer.array();
            String msg = new String(data, Charset.forName("GBK")).trim();
            System.out.print(Thread.currentThread().getName()+"======");
            System.out.println("服务器接收到的消息:"+msg);


//       回写数据
            ByteBuffer out = ByteBuffer.wrap("ok".getBytes());
//           将消息发送给客户端
            channel.write(out);

        }else {
            System.out.println("客户端已经关闭");
            key.cancel();
        }
    }

//    启动服务端测试
    public static void main(String[] args) throws IOException {
     NIOServer server = new NIOServer();
     server.initServer(10020);
     server.listen();
    }
}



2.2 客户端模拟

客户端模拟,每秒中发送一个暑假给服务端,分别为嘻嘻,哈哈,嘎嘎

在这里插入图片描述

2.3 并发结果

服务端启动成功!!!
新的客户端连接上来。。。

新的客户端连接上来。。。

新的客户端连接上来。。。

main======服务器接收到的消息:嘻嘻嘻嘻
main======服务器接收到的消息:嘎嘎嘎
main======服务器接收到的消息:哈哈哈
main======服务器接收到的消息:嘻嘻嘻嘻
main======服务器接收到的消息:嘎嘎嘎
main======服务器接收到的消息:哈哈哈
main======服务器接收到的消息:嘻嘻嘻嘻
main======服务器接收到的消息:嘎嘎嘎
main======服务器接收到的消息:哈哈哈
main======服务器接收到的消息:嘻嘻嘻嘻
main======服务器接收到的消息:嘎嘎嘎
main======服务器接收到的消息:哈哈哈
main======服务器接收到的消息:嘻嘻嘻嘻
main======服务器接收到的消息:嘎嘎嘎
main======服务器接收到的消息:哈哈哈
main======服务器接收到的消息:嘻嘻嘻嘻
main======服务器接收到的消息:嘎嘎嘎
2.4 结论

我们不难看出,在三个客户端时候连上来的时候,我们还是使用同一个线程。这样我们对于服务器的开销就很小。且运行效率要搞的很。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值