Java NIO - 非阻塞 IO和复用

      传统的IO一般都是阻塞的。例如传统的socket的连接是阻塞的,在服务器等待客户端连接的时候和当客户端连接得到一个独立的线程处理客户端的操作。java nio是非阻塞的,在一个线程不须要等待读和写事件。它主要是复用,用下面一个概念来解释:想象一下你进一个餐厅里有一个桌子,一个服务员。如果餐厅经理安排每一个桌子一个服务员,餐厅就很容易停业了。然而一个服务员可以负责多张桌子而不是持续的等待一张桌子。在我们看代码之前,让我们先看看一些基本类,该类构成了“Selector”框架。
   重要的类
   NetworkChannel:是一个网络通道,bind方法可以绑定本地的一个地址
   SelectableChannel:定义方法channel()通过一个Selector能够被进行多路复用(与其他通道一起使用)可选择的通道可以由多个线程使用,一个可选择的通道可能在阻塞或非阻塞模式下操作使用。
  Selector:处理多selectablechannels组合,一个selectablechannel注册到选择器.这个注册会被SelectionKey所呈现。一个选择器能够遍历所有的key,同时也能够选定的键和取消的键。操作系统检查每个通道的准备情况。只要一个通道是准备好了它的关键是投入到选定的关键。选择器可以使用它们的选择键来使用并发线程。
  AbstactSelectableChannel:基本实现了可选择通道。它包含了registering,deregistering和close通道。能够判断通道是否被阻塞和保存当前key的列表。
  SocketChannel:它代表是一个socket通道。这样的通道可以选择。Socket通道支持非阻塞连接。Socket通道是线程安全的

我们现在看一个例子使用上面的类。服务端socket监听客户端的连接。

server:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
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.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
 
public class ServerSocketChannelExample {
    private static String clientChannel = "clientChannel";
    private static String serverChannel = "serverChannel";
    private static String channelType = "channelType";
 
    /**
     * ServerSocketChannel represents a channel for sockets that listen to
     * incoming connections.
     *
     * @throws IOException
     */
    public static void main(String[] args) throws IOException {
        int port = 4444;
        String localhost = "localhost";
 
        // create a new serversocketchannel. The channel is unbound.
        ServerSocketChannel channel = ServerSocketChannel.open();
 
        // bind the channel to an address. The channel starts listening to
        // incoming connections.
        channel.bind(new InetSocketAddress(localhost, port));
 
        // mark the serversocketchannel as non blocking
        channel.configureBlocking(false);
 
        // create a selector that will by used for multiplexing. The selector
        // registers the socketserverchannel as
        // well as all socketchannels that are created
        Selector selector = Selector.open();
 
        // register the serversocketchannel with the selector. The OP_ACCEPT
        // option marks
        // a selection key as ready when the channel accepts a new connection.
        // When the
        // socket server accepts a connection this key is added to the list of
        // selected keys of the selector.
        // when asked for the selected keys, this key is returned and hence we
        // know that a new connection has been accepted.
        SelectionKey socketServerSelectionKey = channel.register(selector,
                SelectionKey.OP_ACCEPT);
        // set property in the key that identifies the channel
        Map<String, String> properties = new HashMap<String, String>();
        properties.put(channelType, serverChannel);
        socketServerSelectionKey.attach(properties);
        // wait for the selected keys
        for (;;) {
 
            // the select method is a blocking method which returns when atleast
            // one of the registered
            // channel is selected. In this example, when the socket accepts a
            // new connection, this method
            // will return. Once a socketclient is added to the list of
            // registered channels, then this method
            // would also return when one of the clients has data to be read or
            // written. It is also possible to perform a nonblocking select
            // using the selectNow() function.
            // We can also specify the maximum time for which a select function
            // can be blocked using the select(long timeout) function.
            if (selector.select() == 0)
                continue;
            // the select method returns with a list of selected keys
            Set<SelectionKey> selectedKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectedKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                // the selection key could either by the socketserver informing
                // that a new connection has been made, or
                // a socket client that is ready for read/write
                // we use the properties object attached to the channel to find
                // out the type of channel.
                if (((Map<!--?, ?-->) key.attachment()).get(channelType).equals(
                        serverChannel)) {
                    // a new connection has been obtained. This channel is
                    // therefore a socket server.
                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key
                            .channel();
                    // accept the new connection on the server socket. Since the
                    // server socket channel is marked as non blocking
                    // this channel will return null if no client is connected.
                    SocketChannel clientSocketChannel = serverSocketChannel
                            .accept();
 
                    if (clientSocketChannel != null) {
                        // set the client connection to be non blocking
                        clientSocketChannel.configureBlocking(false);
                        SelectionKey clientKey = clientSocketChannel.register(
                                selector, SelectionKey.OP_READ,
                                SelectionKey.OP_WRITE);
                        Map<String, String> clientproperties = new HashMap<String, String>();
                        clientproperties.put(channelType, clientChannel);
                        clientKey.attach(clientproperties);
 
                        // write something to the new created client
                        CharBuffer buffer = CharBuffer.wrap("Hello client");
                        while (buffer.hasRemaining()) {
                            clientSocketChannel.write(Charset.defaultCharset()
                                    .encode(buffer));
                        }
                        buffer.clear();
                    }
 
                } else {
                    // data is available for read
                    // buffer for reading
                    ByteBuffer buffer = ByteBuffer.allocate(20);
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    int bytesRead = 0;
                    if (key.isReadable()) {
                        // the channel is non blocking so keep it open till the
                        // count is >=0
                        if ((bytesRead = clientChannel.read(buffer)) > 0) {
                            buffer.flip();
                            System.out.println(Charset.defaultCharset().decode(
                                    buffer));
                            buffer.clear();
                        }
                        if (bytesRead < 0) {
                            // the key is automatically invalidated once the
                            // channel is closed
                            clientChannel.close();
                        }
                    }
 
                }
 
                // once a key is handled, it needs to be removed
                iterator.remove();
 
            }
        }
 
    }
}
client:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
 
public class SocketChannelExample {
     
    public static void main(String[] args) throws IOException,
            InterruptedException {
        int port = 4444;
        SocketChannel channel = SocketChannel.open();
 
        // we open this channel in non blocking mode
        channel.configureBlocking(false);
        channel.connect(new InetSocketAddress("localhost", port));
 
        while (!channel.finishConnect()) {
            // System.out.println("still connecting");
        }
        while (true) {
            // see if any message has been received
            ByteBuffer bufferA = ByteBuffer.allocate(20);
            int count = 0;
            String message = "";
            while ((count = channel.read(bufferA)) > 0) {
                // flip the buffer to start reading
                bufferA.flip();
                message += Charset.defaultCharset().decode(bufferA);
 
            }
 
            if (message.length() > 0) {
                System.out.println(message);
                // write some data into the channel
                CharBuffer buffer = CharBuffer.wrap("Hello Server");
                while (buffer.hasRemaining()) {
                    channel.write(Charset.defaultCharset().encode(buffer));
                }
                message = "";
            }
 
        }
    }
}

原文地址: http://www.studytrails.com/java-io/non-blocking-io-multiplexing.jsp

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值