Java NIO - Socket 通道

  • 简述

    Java NIO套接字通道是一个可选类型的通道,这意味着它可以使用选择器进行多路复用,用于面向流的数据流连接套接字。套接字通道可以通过调用其静态 open() 方法创建,前提是任何预先存在的套接字尚不存在。套接字通道是通过调用 open 方法创建的,但尚未连接。为了连接套接字通道,将调用connect() 方法。这里要提到的一点是,如果通道未连接,并且尝试尝试尝试任何 I/O 操作,则此通道将引发 NotYet连接异常。因此,在执行任何IO操作之前,必须确保通道已连接。一旦通道被连接,它就会保持连接状态,直到它被关闭。套接字通道的状态可以通过调用其 isConnected 方法来确定。
    套接字通道的连接可以通过调用其finishConnect() 方法来完成。连接操作是否正在进行中可以通过调用“连接挂起”方法来确定。默认情况下,套接字通道支持非阻塞连接。此外,它还支持异步关闭,这类似于 Channel 类中指定的异步关闭操作。
    套接字通道对于多个并发线程使用是安全的。它们支持并发读取和写入,尽管在任何给定时间最多可能有一个线程正在读取,并且最多一个线程可能正在写入。连接和 finishConnect 方法相互同步,并且在调用这些方法之一时尝试启动读取或写入操作将被阻止,直到该调用完成。
  • 套接字通道的重要方法

    • bind(SocketAddress local) − 此方法用于将套接字通道绑定到作为此方法的参数提供的本地地址。
    • connect(SocketAddress remote) − 此方法用于将套接字连接到远程地址。
    • finishConnect() − 此方法用于完成连接套接字通道的过程。
    • getRemoteAddress() − 此方法返回通道套接字连接到的远程位置的地址。
    • isConnected() − 如前所述,此方法返回套接字通道的连接状态,即它是否连接。
    • open() 和 open((SocketAddress remote) − open 方法用于打开没有指定地址的套接字通道,而参数化开放方法为指定的远程地址打开通道并连接到它。这种方便的方法的工作方式就好像是调用 open() 方法,在生成的套接字通道上调用 connect 方法,远程传递它,然后返回该通道。
    • read(ByteBuffer dst) − 此方法用于通过套接字通道从给定缓冲区读取数据。
    • isConnectionPending() − 此方法指示此通道上是否正在进行连接操作。
  • 下面的示例演示如何从 Java NIO 套接字通道发送数据。

    C:/Test/temp.txt

    
    Hello World!
    

    客户端:SocketChannelClient.java

    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.nio.file.StandardOpenOption;
    import java.util.EnumSet;
    public class SocketChannelClient {
       public static void main(String[] args) throws IOException {
          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();
       }
    }
    

    输出

    在服务器启动之前,运行客户端不会打印任何内容。
    
    

    服务器:SocketChannelServer.java

    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.net.SocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.FileChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    public class SocketChannelServer {
       public static void main(String[] args) throws IOException {
          SocketChannel server = SocketChannel.open();
          SocketAddress socketAddr = new InetSocketAddress("localhost", 9000);
          server.connect(socketAddr);
          Path path = Paths.get("C:/Test/temp.txt");
          FileChannel fileChannel = FileChannel.open(path);
          ByteBuffer buffer = ByteBuffer.allocate(1024);
          while(fileChannel.read(buffer) > 0) {
             buffer.flip();
             server.write(buffer);
             buffer.clear();
          }
          fileChannel.close();
          System.out.println("File Sent");
          server.close();
       }
    }
    

    输出

    运行服务器将打印以下内容。
    
    Connection Set:  /127.0.0.1:49558
    File Received