java socket IO和NIO的对比

先上一个最基本的socket io的例子,首先是server端:

ServerSocket serverSocket = new ServerSocket(PORT);
while (true) {
    Socket client = serverSocket.accept();    
    new HandlerThread(client);
}

DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while(true){
    String clientInputStr = input.readUTF();
}

首先new一个ServerSocket,之后循环监听,得到一个新的连接之后,交给一个HandlerThread来处理。

而在HandlerThread当中,也是需要阻塞的方式来读取输入,读不着了就等着。

然后是client端:

socket = new Socket(IP_ADDR, PORT);

DataInputStream input = new DataInputStream(socket.getInputStream());
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while(true){
    String str = new BufferedReader(new InputStreamReader(System.in)).readLine();
    out.writeUTF(str);
    String ret = input.readUTF();
}

而在client当中,大同小异,new一个socket对象,连接到server上,之后向server发信息,然后等着server的响应。

上面3处标红的地方,server监听接入,server和client的读取input stream,都是通过阻塞的方式来完成。如果说,一个server介入了若干个连接,就需要若干个线程来完成和客户端的通信工作。

 

然后我们再上一个java nio的例子,首先是server端:

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
this.selector = Selector.open();

serverChannel.register(selector, SelectionKey.OP_ACCEPT);

 

public void listen() throws Exception {
    while (true) {
        selector.select();
        Iterator ite = this.selector.selectedKeys().iterator();
        while (ite.hasNext()) {
            SelectionKey key = (SelectionKey) ite.next();
            ite.remove();
            if (key.isAcceptable()) {
                ServerSocketChannel server = (ServerSocketChannel) key.channel();
                SocketChannel channel = server.accept();
                channel.configureBlocking(false);
                channel.write(ByteBuffer.wrap(new String("hello client").getBytes()));
                channel.register(this.selector, SelectionKey.OP_READ);

            } else if (key.isReadable()) {
                read(key);
            }

        }
    }
}

private void read(SelectionKey key) throws Exception {
    SocketChannel channel = (SocketChannel) key.channel();
    ByteBuffer buffer = ByteBuffer.allocate(10);
    channel.read(buffer);
    byte[] data = buffer.array();
    String msg = new String(data).trim();
    System.out.println("server receive from client: " + msg);
    ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
    channel.write(outBuffer);
}

基本流程解释一下:

首先是准备阶段,生成一个server channel的对象,然后绑定端口,再打开一个selector,注册accept事件。

然后是监听,监听到一个连接,accept,并返回一个socket channel,把这个客户端channel的read事件注册到selector,注意,这里的selector跟之前监听server channel的是同一个,也就是说,目前这个selector要听2件事,一个是监听室针对server channel,另一个是监听client channel有没有数据进来。

有数据进来的话,读数据,然后写个返回回去。

 

然后是client端:

SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
this.selector = Selector.open();
channel.connect(new InetSocketAddress(ip, port));
channel.register(selector, SelectionKey.OP_CONNECT);

public void listen() throws Exception { // 轮询访问selector
    while (true) {
        selector.select(); // 获得selector中选中的项的迭代器
        Iterator ite = this.selector.selectedKeys().iterator();
        while (ite.hasNext()) {
            SelectionKey key = (SelectionKey) ite.next(); // 删除已选的key,以防重复处理
            ite.remove(); // 连接事件发生
            if (key.isConnectable()) {
                SocketChannel channel = (SocketChannel) key.channel(); // 如果正在连接,则完成连接
                if (channel.isConnectionPending()) {
                    channel.finishConnect();
                } // 设置成非阻塞
                channel.configureBlocking(false);
                channel.write(ByteBuffer.wrap(new String("hello server!").getBytes()));
                channel.register(this.selector, SelectionKey.OP_READ); // 获得了可读的事件
            } else if (key.isReadable()) {
                 read(key);
            }
        }
    }
}

private void read(SelectionKey key) throws Exception {
    SocketChannel channel = (SocketChannel) key.channel();
    ByteBuffer buffer = ByteBuffer.allocate(10);
    channel.read(buffer);
    byte[] data = buffer.array();
    String msg = new String(data).trim();
    System.out.println("client receive msg from server:" + msg);
    ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());
    channel.write(outBuffer);
}

这里也需要简单解释一下流程:

准备阶段,创建一个SocketChannel对象,连接到服务器上,然后打开一个selector,注册connect事件到selector上。

然后同样是监听,监听到服务器已经响应了自己的连接,需要调用一个finishConnect方法,这就算是完成连接了,把read事件注册到selector上。

读到服务器响应,读取数据,返回响应。

 

到此为止,对于io和nio的实现方式,我们都已经有了大体的了解,我们现在来比较一下2种方式的不同点:

1、多路选择器(selector)的使用,使得一个线程可以针对多个channel进行监听,大大的减少线程的数量,提高了系统运行的效率。

2、io是针对流的,而nio针对的是缓冲区,而在缓冲区当中指针可以任意进行移动,对某些复杂的业务场景,可以极大地简化编程的复杂度。

转载于:https://my.oschina.net/dongtianxi/blog/794681

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值