NIO 学习 -Channels

Java NIO - Channels

Java中对Channel的定义

/**
 * A nexus for I/O operations.
 *
 * 

A channel represents an open connection to an entity such as a hardware * device, a file, a network socket, or a program component that is capable of * performing one or more distinct I/O operations, for example reading or * writing. * *

A channel is either open or closed. A channel is open upon creation, * and once closed it remains closed. Once a channel is closed, any attempt to * invoke an I/O operation upon it will cause a {@link ClosedChannelException} * to be thrown. Whether or not a channel is open may be tested by invoking * its {@link #isOpen isOpen} method. * *

Channels are, in general, intended to be safe for multithreaded access * as described in the specifications of the interfaces and classes that extend * and implement this interface. * * * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 */ public interface Channel extends Closeable I / O操作的联系。 通道表示与诸如硬件设备,文件,网络套接字或程序组件之类的实体的开放连接,该实体能够执行一个或多个不同的I / O操作(例如,读取或写入)。 通道是打开的还是关闭的。通道在创建时打开,一旦关闭,它便保持关闭状态。一旦关闭通道,任何在其上调用I / O操作的尝试都将引发a ClosedChannelException 。可以通过调用通道的isOpen方法来测试通道是否打开。 通常,通道的目的是确保多线程访问的安全,如接口规范以及扩展和实现此接口的类中所述。

理解:
channels 和 stream 是有点类似的,但又有些不同:

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  • 通道可以异步地读写。
  • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

Channel是一个接口,Java比较常见的几个实现类:

  • FileChannel 从文件中读写数据。
  • DatagramChannel 能通过UDP读写网络中的数据。
  • SocketChannel 能通过TCP读写网络中的数据。
  • ServerSocketChannel 可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

FileChannel:
我们不能直接获取FileChannel对象,可以通过以下获得:

  • getChannel() -任何FileInputStream,FileOutputStream或RandomAccessFile上的方法。
  • open() -FileChannel的方法,默认情况下打开通道
    FileChannel的对象类型取决于从对象创建中调用的类的类型,即,如果通过调用FileInputStream的getchannel方法创建对象,则打开File通道进行读取,并在尝试写入时抛出NonWritableChannelException。

DatagramChannel、SocketChannel、ServerSocketChannel:
我们不能直接获取对象,通过静态方法open()获取

以文件流为例

//FileInputStream.getChannel();
RandomAccessFile aFile = new RandomAccessFile(FILENAME);
FileChannel inChannel = aFile.getChannel();

//FileChannel.open();
Set<StandardOpenOption> options = new HashSet<> ();
options.add(StandardOpenOption.CREATE);
options.add(StandardOpenOption.APPEND);
Path path = Paths.get("FILENAME");
FileChannel fileChannel = FileChannel.open(path, options);


ByteBuffer buf = ByteBuffer.allocate(48);

int bytesRead = inChannel.read(buf);
while (bytesRead != -1) {
     

System.out.println("Read " + bytesRead);
buf.flip();

while(buf.hasRemaining()){
     
System.out.print((char) buf.get());
}

buf.clear();
bytesRead = inChannel.read(buf);
}
aFile.close();

DatagramChannel

https://docs.oracle.com/javase/8/docs/api/java/nio/channels/DatagramChannel.html

通过调用open此类的方法之一来创建数据报通道。无法为任意现有的数据报套接字创建通道。新建的数据报通道已打开但未连接。无需连接数据报通道即可使用send和receive方法。可以通过调用数据报通道的connect方法来连接数据报通道,以避免安全检查的开销,否则将作为每个发送和接收操作的一部分执行安全检查。为了使用read和write方法,必须连接一个数据报通道,因为这些方法不接受也不返回套接字地址。

连接后,数据报通道将保持连接状态,直到断开连接或关闭为止。可以通过调用数据报信道的isConnected方法来确定是否连接了数据报信道。

重要方法:

  • bind(SocketAddress local)
    将通道的套接字绑定到本地地址。此方法用于在套接字和本地地址之间建立关联。建立关联后,套接字将保持绑定状态,直到关闭通道。如果local参数为null,则套接字将绑定到自动分配的地址。
  • connect(SocketAddress remote)
    远程连接此地址的套接字,通道仅从指定的连接中发送/接受数据。数据报套接字一直保持连接状态,直到显式断开连接或将其关闭为止。
  • disconnect()
    断开该通道的链接
  • getRemoteAddress()
    获取远程连接地址
  • isConnected()
    判断通道是否已经连接
  • open()
    通过调用系统范围的默认SelectorProvider对象的openDatagramChannel方法来创建新通道,这个通道不能被连接。
  • read(ByteBuffer dst)
    通过数据报通道从给定的缓冲区读取数据报。仅当Socket is connected时才能调用此方法,并且它仅接受来自socket’s peer(套接字对等方)的数据。如果数据报中的字节数超过给定缓冲区中剩余的字节数,则数据报的其余部分将被静默丢弃。
  • write()
    将数据报写入此通道。仅当Socket is connected时才能调用此方法,它将数据报直接发送到socket’s peer(套接字对等方)。
  • receive(ByteBuffer dst)
    通过此通道接收数据报。
  • send(ByteBuffer src,SocketAddress target)
    通过此通道发送数据报。

DatagramChannel example :

DatagramChannelServer

        DatagramChannel server = DatagramChannel.open();
        InetSocketAddress iAdd = new InetSocketAddress("localhost", 8989);
        //绑定localhost:8989
        server.bind(iAdd);
        System.out.println("Server Started: " + iAdd);
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        //运行之后,一直处于监听的状态,只有client连接发送数据之后,才会继续执行。

        //监听接收数据报
        SocketAddress remoteAdd = server.receive(buffer);
        //翻转此缓冲区
        buffer.flip();
        int limits = buffer.limit();
        byte bytes[] = new byte[limits];
        //将buffer中的数据写入到bytes中
        buffer.get(bytes, 0, limits);
        String msg = new String(bytes);
        System.out.println("Client at " + remoteAdd + "  sent: " + msg);
        //修改buffer中9到11的数据
        buffer.put ( 9,(byte)100 );
        buffer.put ( 10,(byte)100 );
        buffer.put ( 11,(byte)100 );
        //测试,发送的数据是从pos开始的
        buffer.position (9);
        //由send方法的返回值可以断定,发送的是pos 到 cap 之间的数据
        int send = server.send ( buffer, remoteAdd );
        server.close();

输出:

Server Started: localhost/127.0.0.1:8989
Client at /127.0.0.1:53795  sent: Hello World!

DatagramChannelClient

        DatagramChannel client = null;
        client = DatagramChannel.open();

        client.bind(null);

        String msg = "Hello World!";
        ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes());
        InetSocketAddress serverAddress = new InetSocketAddress("localhost",
                8989);

        int send = client.send ( buffer, serverAddress );
        //相当于对通道进行put操作。 所以需要flip
        client.receive(buffer);
        // flip理解
        // 比如send的是 buffer position = 9 cap =12 ,那么发送过来应该就是 9 10 11 ,
        // 但是 接受到的是buffer position =3 cap =12
        buffer.flip();
        while(buffer.hasRemaining ()){
     
            System.out.println((char)buffer.get ());
        }
        client.close();

输出:

d
d
d

SocketChannel

定义:套接字通道是一个可选类型的通道,面向流的数据流连接套接字。
通过调用其静态open()来创建套接字通道。无法为任意现有的套接字创建通道。新建的套接字通道已打开,但尚未连接。尝试在未连接的通道上调用I/O操作将导致NotYetConnectedException抛出。
套接字通道可以通过调用其connect()进行连接;连接后,套接字通道将保持连接状态,直到关闭为止。
套接字通道是否已连接可以通过调用其isConnected方法来确定。

可以通过调用其finishConnect()方法来完成套接字通道的连接。可以通过调用isConnectionPending方法来确定是否正在进行连接操作。默认情况下,套接字通道支持非阻塞连接。
它还支持异步关闭,这类似于Channel类中指定的异步关闭操作。

套接字通道可以安全地供多个并发线程使用。它们支持并发读取和写入,尽管在任何给定时间最多可以读取一个线程,并且最多可以写入一个线程。connect和finishConnect方法彼此相互同步,并且在这些方法之一的调用正在进行时尝试启动读取或写入操作将被阻止,直到该调用完成为止。

重要方法:

  • bind(SocketAddress local) -此方法用于将套接字通道绑定到作为此方法的参数提供的本地地址。

  • connect(SocketAddress remote) -此方法用于将套接字连接到远程地址。

  • finishConnect() -此方法用于完成套接字通道的连接过程。

  • getRemoteAddress() -此方法返回通道套接字连接到的远程位置的地址。

  • isConnected() -如前所述,此方法返回套接字通道的连接状态,即是否已连接。

  • open()和open((SocketAddress remote) -使用open方法打开没有指定地址的套接字通道,同时使用参数化open方法打开指定远程地址的通道并也连接到该通道。此便捷方法的工作方式就像调用open( )方法,在生成的套接字通道上调用connect方法,将其远程传递,然后返回该通道。

  • read(ByteBuffer dst) -此方法用于通过套接字通道从给定的缓冲区读取数据。

  • isConnectionPending() -此方法指示此通道上是否正在进行连接操作。
    SocketChannelClient


      ServerSocketChannel serverSocket = null;
      SocketChannel client = null;
      serverSocket = ServerSocketChannel.open();
      serverSocket.socket().bind(new InetSocketAddress(9000));
      client = serverSocket.accept();
      System.out.println("Connection Set:  " + client.getRemoteAddress());
      Path path = Paths.get("C:/Test/temp1.txt");
      FileChannel fileChannel = FileChannel.open(path, 
         EnumSet.of(StandardOpenOption.CREATE, 
            StandardOpenOption.TRUNCATE_EXISTING,
            StandardOpenOption.WRITE)
         );      
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      while(client.read(buffer) > 0) {
     
         buffer.flip();
         fileChannel.write(buffer);
         buffer.clear();
      }
      fileChannel.close();
      System.out.println("File Received");
      client.close();

你可能感兴趣的:(Java,SE,java,nio)