1.SocketChannel
SocketChannel是JavaNIO中连接到TCP网络的管道,功能和Java network包的sockt相关接口一样;有两种创建一个SocketChannel的方法:
- 打开一个SocketChannel连接到某个服务器
- 当一个ServerSocketChannel接收到请求的时候回自动创建一个SocketChannel
1.1.打开SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connnet(new InetSocketAddress("http://www.baidu.com", 80));
1.2.关闭SocketChannel
socketChannel.close();
1.3.读取数据
ByteBuffer buffer = ByteBuffer.allocate(48);
int len = socketChannel.read(buffer);
通过前边的了解,我们对于使用buffer从channel中读取数据应该已经非常熟悉了,read()方法会返回一个整数结果表示读入数据的长度,当返回-1时表示到达数据流的终点(链接关闭)。
1.4.写入数据
String newData = "新的数据..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
channel.write(buf);
}
这里我们使用了一个循环调用write方法,因为我们并不确定write方法写入channel了多少数据,所以我们循环调用write方法直到buffer没有可写数据了。
1.5.非阻塞模式
SocketChannel可以设置为非阻塞模式(non-blocking mode),设置为非阻塞模式后,就可以使用异步模式调用connect,write,read方法了。
connect
在异步模式下,调用connet()方法后可能在链接没有建立前就返回,为了确定链接是否建立成功我们需要调用finishConnect()方法:
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress("http://localhost", 80));
while(!socketChannel.finishConnect()){
// 等待或执行其他操作
...
}
write
在非阻塞模式下调用write方法可能没有任何返回值,因此我们需要循环调用write方法,就像上边的列子一样。
read
在非阻塞模式下,read方法可能不会读取出任何数据,所以我们需要关注read方法的返回值,可以通过返回值确定读取了多少数据。
2.ServerSocketChannel
ServerSocketChannel是可以监听TCP链接的通道,和Java网络包中的ServerSocket作用很相似。
下面是一个简单的例子
// 1. 打开一个ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 2. 绑定到需要监听的端口
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
boolean listening = true;
while(listening){
System.out.println("listening...");
// 3. 监听请求(阻塞,直到接收到请求)
SocketChannel socketChannel = serverSocketChannel.accept();
System.out.println("accept request");
// 4. 执行其他操作 ...
// 5. 关闭通道
serverSocketChannel.close();
}
ServerSocketChannel可以设置为非阻塞模式,在非阻塞模式中accept()方法立即返回,所以如果没有请求可能返回null,所以我们需要像下边的例子判断返回
SocketChannel是否为null
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
serverSocketChannel.configureBlocking(false);
while (true){
SocketChannel socketChannel = serverSocketChannel.accept();
if(socketChannel != null){
// 执行操作
}
}
3.DatagramChannel
DatagramChannel是可以接收和发送UDP数据包的Channel。因为UDP是无连接的网络连接协议,我们无法像其他channel一样使用默认的读写方法操作DatagramChannel中的数据,替代方法是send和receive数据包。
3.1.打开一个DatagramChannel
// 打开DatagramChannel
DatagramChannel channel = DatagramChanne.open();
// 绑定到UDP端口9999
channel.bind(new InetSocketAddress(9999));
3.2.接收数据
// 申请buffer
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
// 接收数据
channel.receive(buf);
receive()方法将复制接收到的数据到buffer中,如果接收的数据包中的数据大于buffer的容量,多余的数据则会被丢弃!
3.3.发送数据
String data = "发送数据..." + System.currentTimeMillis();
buffer.clear();
buffer.put(data.getBytes());
buffer.flip();
int sent = channel.send(buffer, new InetSocketAddress("localhost", 80));
上边代码演示了向本地UDP80接口发送数据,事实上没有应用在监听这个端口,所以什么都不会发生。由于UDP协议的无连接特性,无论发送的数据包是否被接收到,我们都不会得到反馈。
3.4.连接到特定地址
channel.connect(new InetSocketAddress("www.baidu.com", 80));
DatagramChannel可以调用connect方法链接到特定的地址,我们上面讲过UDP是无连接的协议,所以这里的链接方式并不是像TCP链接一样的真正的链接;但是它锁定了DatagramChannel使它只能向特定的地址发送和接收数据。
当链接之后DatagramChannel就可以像其他channel一样调用read()和write()方法了。
int bytesRead = channel.read(buffer);
int bytesWritten = channel.write(buffer);