• NIO:与 Buffer 一起使用 Channel


    如前文所述,Channel实例代表了一个与设备的连接,通过它可以进行输入输出操作。实际上Channel的基本思想与我们见过的普通套接字非常相似。对于TCP协议,可以使用ServerSocketChannelSocketChannel。还有一些针对其他设备的其他类型信道(如,FileChannel),尽管我们在后文中不会再提及,这里介绍的大部分内容对于它们同样适用。信道(channel)和套接字(socket)之间的不同点之一,可能是信道通常要调用静态工厂方法来获取实例:

    SocketChannel clntChan = SocketChannel.open();

    ServerSocketChannel servChan =

    ServerSocketChannel.open();

     Channel使用的不是流,而是缓冲区来发送或读取数据。Buffer类或其任何子类的实例都可以看作是一个定长的Java基本数据类型元素序列。与流不同,缓冲区有固定的、有限的容量,并由内部(但可以被访问)状态记录了有多少数据放入或取出,就像是有限容量的队列一样。Buffer是一个抽象类,只能通过创建它的子类来获得Buffer实例,而每个子类都设计为用来容纳一种Java基本数据类型(boolean除外)。因此,这些实例分别为FloatBufferIntBuffer,或ByteBuffer,等等(ByteBuffer是这些实例中最灵活的,并将在后面很多例子中用到)。在channel中使用Buffer实例通常不是使用构造函数创建的,而是通过调用allocate()方法创建指定容量的Buffer实例,

    ByteBuffer buffer = ByteBuffer.allocate(CAPACITY);

    或通过包装一个已有的数组来创建:

    ByteBuffer buffer = ByteBuffer.wrap(byteArray); 

     

    NIO的强大功能部分来自于channel的非阻塞特性。回顾前面介绍的内容可以知道,套接字的某些操作可能会无限期地阻塞。例如,对accept()方法的调用可能会因为等待一个客户端连接而阻塞;对read()方法的调用可能会因为没有数据可读而阻塞,直到连接的另一端传来新的数据。总的来说,创建/接收连接或读写数据等I/O调用,都可能无限期地阻塞等待,直到底层的网络实现发生了什么。慢速的、有损耗的网络,或仅仅是简单的网络故障都可能导致任意时间的延迟。然而不幸的是,在调用一个方法之前无法知道其是否会阻塞。NIOchannel抽象的一个重要特征就是可以通过配置它的阻塞行为,以实现非阻塞式的信道。

    clntChan.configureBlocking(false);

     

    在非阻塞式信道上调用一个方法总是会立即返回。这种调用的返回值指示了所请求的操作完成的程度。例如,在一个非阻塞式ServerSocketChannel上调用accept()方法,如果有连接请求在等待,则返回客户端SocketChannel,否则返回null。下面我们来创建一个非阻塞TCP回显客户端。可能阻塞的I/O操作包括建立连接,读和写。通过使用非阻塞式信道,这些操作都将立即返回。我们必须反复调用这些操作,直到所有I/O操作都成功完成。

     

    TCPEchoClientNonblocking.java

    0 import java.net.InetSocketAddress;

    1 import java.net.SocketException;

    2 import java.nio.ByteBuffer;

    3 import java.nio.channels.SocketChannel;

    4

    5 public class TCPEchoClientNonblocking {

    6

    7 public static void main(String args[]) throws Exception

    {

    8

    9 if ((args.length < 2) || (args.length > 3)) // Test for

    correct # of args

    10 throw new IllegalArgumentException("Parameter(s):

    <Server> <Word> [<Port>]");

    11

    12 String server = args[0]; // Server name or IP address

    13 // Convert input String to bytes using the default

    charset

    14 byte[] argument = args[1].getBytes();

    15

    16 int servPort = (args.length == 3) ?

    Integer.parseInt(args[2]) : 7;

    17

    18 // Create channel and set to nonblocking

    19 SocketChannel clntChan = SocketChannel.open();

    20 clntChan.configureBlocking(false);

    21

    22 // Initiate connection to server and repeatedly poll

    until complete

    23 if (!clntChan.connect(new InetSocketAddress(server,

    servPort))) {

    24 while (!clntChan.finishConnect()) {

    25 System.out.print("."); // Do something else

    26 }

    27 }

    28 ByteBuffer writeBuf = ByteBuffer.wrap(argument);

    29 ByteBuffer readBuf =

    ByteBuffer.allocate(argument.length);

    30 int totalBytesRcvd = 0; // Total bytes received so far

    31 int bytesRcvd; // Bytes received in last read

    32 while (totalBytesRcvd < argument.length) {

    33 if (writeBuf.hasRemaining()) {

    34 clntChan.write(writeBuf);

    35 }

    36 if ((bytesRcvd = clntChan.read(readBuf)) == -1) {

    37 throw new SocketException("Connection closed

    prematurely");

    38 }

    39 totalBytesRcvd += bytesRcvd;

    40 System.out.print("."); // Do something else

    41 }

    42

    43 System.out.println("Received: " + // convert to String

    per default charset

    44 new String(readBuf.array(), 0, totalBytesRcvd));

    45 clntChan.close();

    46 }

    47 }

     

    TCPEchoClientNonblocking.java

     1.获取并转换参数:第9-16

    2. 创建非阻塞式SocketChannel:第19-20

    3.连接服务器:第23-27 

    由于该套接字是非阻塞式的,因此对connect()方法的调用可能会在连接建立之前返回,如果在返回前已经成功建立了连接,则返回true,否则返回false。对于后一种情况,任何试图发送或接收数据的操作都将抛出NotYetConnectedException异常,因此,我们通过持续调finishConnect()方法来"轮询"连接状态,该方法在连接成功建立之前一直返回false。打印操作演示了在等待连接建立的过程中,程序还可以执行其他任务。不过,这种忙等的方法非常浪费系统资源,这里这样做只是为了演示该方法的使用。 

    4.创建读写缓冲区:第28-29

     我们分别使用了两种方法来创建将要用来读写数据的ByteBuffer实例。一是通过包装包含了要发送数据的byte[]数组,另一个方法是调用allocate()方法,创建具有与前面byte[]组大小相同缓冲区的ByteBuffer实例。

     5.反复循环直到发送和接收完所有字节:第32-41

    只要输出缓冲区中还留有数据,就调用write()方法。对read()方法的调用不会阻塞等待,但是当没有数据可读时该方法将返回0。这里,打印语句再次举例说明了在等待通信完成的过程中,程序可以执行其他任务。

    6.打印接收到的数据:第43-44

    7.关闭信道:第45

    与套接字类似,信道在完成其任务后也需要关闭。

    相关下载:

    Java_TCPIP_Socket编程(doc)

    http://download.csdn.net/detail/undoner/4940239

     

    文献来源:

    UNDONER(小杰博客) :http://blog.csdn.net/undoner

    LSOFT.CN(琅软中国) :http://www.lsoft.cn

  • 相关阅读:
    CentOS 6.9/7通过yum安装指定版本的MySQL
    CentOS的el5, el6, el7代表什么
    MySQL的mysql.sock文件作用(转)
    MySQL常用命令
    Linux下以特定用户运行命令
    简述TCP的三次握手过程
    Tomcat-connector的微调(1): acceptCount参数
    tomcat修改jsessionid在cookie中的名称
    使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案【享学Spring】
    [LeetCode] Construct the Rectangle 构建矩形
  • 原文地址:https://www.cnblogs.com/wuyida/p/6301059.html
Copyright © 2020-2023  润新知