前面 我们介绍了 FileChannel,本篇我们来一起学习 SocketChannel, ServerSocketChannel 与 DatagramChannel。
ServerSocketChannel Java NIO ServerSocketChannel 是用于监听 TCP 端口连接的通道,与 Java 标准 IO 中的 ServerSocket 一样。ServerSocketChannel 位于 java.nio.channels
包下。
例如:
1 2 3 4 5 6 7 8 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress (9999 )); while (true ){ SocketChannel socketChannel = serverSocketChannel.accept(); }
打开 ServerSocketChannel 通过调用 ServerSocketChannel.open()
方法,即可打卡一个 ServerSocketChannel:
1 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
关闭 ServerSocketChannel 调用 serverSocketChannel 中的 close()
方法,即可关闭:
监听传入连接 通过调用 ServerSocketChannel.accept()
方法,我们可以来监听 TCP 端口上的连接。当 accept () 返回时,它将返回带有传入连接的 SocketChannel。因此,accept () 方法会一直阻塞到有传入连接到达位置。
由于您通常不想只听一个连接,所以在 while 循环中调用 accept ()。如下:
1 2 3 4 while (true ){ SocketChannel socketChannel = serverSocketChannel.accept(); }
Non-blocking 模式 ServerSocketChannel 可以设置为 non-blocking 模式,即使没有传入连接达到,accept () 方法也会立即返回。因此,你需要检查返回的 SocketChannel 是否为 null。例如:
1 2 3 4 5 6 7 8 9 10 11 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.socket().bind(new InetSocketAddress (9999 )); serverSocketChannel.configureBlocking(false ); while (true ){ SocketChannel socketChannel = serverSocketChannel.accept(); if (socketChannel != null ){ } }
SocketChannel Java NIO SocketChannel 是用于连接 TCP 网络 Socket 的通道。有两种方式可以来创建 SocketChannel:
打开一个 SocketChannel,并连接到网络上的某个服务的 TCP 端口上。
在 ServerSocketChannel 中提到过,当有传入连接进来是,ServerSocketChannel 的 accpet () 方法可以获得 SocketChannel.
打开 SocketChannel 1 2 SocketChannel socketChannel = SocketChannel.open();socketChannel.connect(new InetSocketAddress ("127.0.0.1" , 80 ));
关闭 SocketChannel
读取数据 从 socketchannel 中读取数据到 byteBuffer 中。
1 2 3 ByteBuffer buf = ByteBuffer.allocate(48 );int bytesRead = socketChannel.read(buf);
写入数据 1 2 3 4 5 6 7 8 9 10 11 String newData = "New String to write to file..." + System.currentTimeMillis();ByteBuffer buf = ByteBuffer.allocate(48 );buf.clear(); buf.put(newData.getBytes()); buf.flip(); while (buf.hasRemaining()) { channel.write(buf); }
Non-blocking 模式 我们可以设置 SocketChannel 为 非阻塞模式,这样的话, 你就可以用异步模式来调用 connect()
, read()
和 write()
方法。
connect() 如果 SocketChannel 处于 非阻塞模式,并且调用了 connect () 方法,这个方法会在连接建立之前返回。我们可以通过调用 finishConnect () 来判断连接是否建立。像这样:
1 2 3 4 5 6 socketChannel.configureBlocking(false ); socketChannel.connect(new InetSocketAddress ("127.0.0.1" , 80 )); while (! socketChannel.finishConnect() ){ }
write() 在非阻塞模式下,write () 方法可能在没有写入任何内容的情况下就返回了。 因此,您需要在循环中调用 write () 方法。
read() 在非阻塞模式下,read()
方法可能在没有读取任何数据的情况下就返回了。因此,您需要注意返回的 int,它会告诉读取了多少字节。
示例 见 前面 文章中的示例代码。
DatagramChannel DatagramChannle 是一个可以用于发送和接收 UDP 数据包的通道。由于 UDP 是一种无连接的网络协议,因此我们不能像在其他通道中那样默认读取和写入 DatagramChannel,而是发送和接收数据包。
打开 DatagramChannel 在 UDP 端口 9999 上接收数据包:
1 2 DatagramChannel channel = DatagramChannel.open();channel.socket().bind(new InetSocketAddress (9999 ));
接收数据 1 2 3 4 ByteBuffer buf = ByteBuffer.allocate(48 );buf.clear(); channel.receive(buf);
receive()
方法将会把收到的数据包中的内容复制到 Buffer 中。如果接收到的数据比 Buffer 的容量还大,多余的数据会默认丢弃掉。
发送数据 1 2 3 4 5 6 7 8 String newData = "New String to write to file..." + System.currentTimeMillis(); ByteBuffer buf = ByteBuffer.allocate(48 );buf.clear(); buf.put(newData.getBytes()); buf.flip(); int bytesSent = channel.send(buf, new InetSocketAddress ("127.0.0.1" , 80 ));
此示例将字符串发送到 UDP 端口 80 上的 “127.0.0.1” 服务器。但是没有任何内容正在侦听该端口,因此不会发生任何事情。 由于 UDP 不对数据传送做出任何保证,因此不会通知你是否收到了发送数据包。
示例 我们用 DatagramChannel 来实现一下 EchoServer。
DatagramSocketEchoServer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package one.wangwei.java.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.net.SocketAddress;import java.nio.ByteBuffer;import java.nio.channels.DatagramChannel;public class DatagramSocketEchoServer { public static void main (String[] args) { try { DatagramChannel server = DatagramChannel.open(); InetSocketAddress sAddr = new InetSocketAddress ("127.0.0.1" , 8989 ); server.bind(sAddr); ByteBuffer byteBuffer = ByteBuffer.allocate(1024 ); while (true ) { System.out.println("Waiting for a message from a remote host at " + sAddr); SocketAddress remoteAddr = server.receive(byteBuffer); byteBuffer.flip(); int limits = byteBuffer.limit(); byte [] bytess = new byte [limits]; byteBuffer.get(bytess, 0 , limits); String msg = new String (bytess); System.out.println("Client at " + remoteAddr + " says: " + msg); byteBuffer.rewind(); server.send(byteBuffer, remoteAddr); byteBuffer.clear(); } } catch (IOException e) { e.printStackTrace(); } } }
DatagramSocketEchoClient 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package one.wangwei.java.nio;import java.io.IOException;import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.DatagramChannel;public class DatagramSocketEchoClient { public static void main (String[] args) { try { DatagramChannel client = DatagramChannel.open(); client.bind(null ); String msg = "hello" ; ByteBuffer buffer = ByteBuffer.wrap(msg.getBytes()); InetSocketAddress serverAddress = new InetSocketAddress ("127.0.0.1" , 8989 ); client.send(buffer, serverAddress); buffer.clear(); client.receive(buffer); buffer.flip(); int limits = buffer.limit(); byte [] bytes = new byte [limits]; buffer.get(bytes, 0 , limits); String response = new String (bytes); System.out.println("Server response: " + response); client.close(); } catch (IOException e) { e.printStackTrace(); } } }
参考资料