- Channel 是一个对象,可以通过它读取和写入数据。
- 拿 NIO 与原来的 I/O 做个比较,通道就像是流。
- 所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。
- 同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
package com.yangmin.study.io.nio.channel;
import java.io.*;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class ChannelTest {
public static void main(String[] args) {
ChannelTest channelTest = new ChannelTest();
// channelTest.fileChannelRead();
// channelTest.fileChannelWrite();
channelTest.transferFrom();
}
void fileChannelRead() {
FileInputStream fileInputStream = null;
FileChannel channel = null;
try {
fileInputStream = new FileInputStream("src/main/resources/read.text");
channel = fileInputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(14);
while (channel.read(buffer) != -1) {
//第一次进来的时候,buffer为:[pos=7 lim=14 cap=14]
System.out.println(buffer.toString());
System.out.println(new String(buffer.array()));
buffer.clear();
}
System.out.println(channel.position());
System.out.println(channel.size());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
void fileChannelWrite() {
FileOutputStream fileOutputStream = null;
FileChannel channel = null;
try {
fileOutputStream = new FileOutputStream("src/main/resources/write.text");
channel = fileOutputStream.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
buffer.put(("注意 FileChannel.write()是在 while 循环中调用的。因为无法保证 write()方法一次能向 " +
"FileChannel 写入多少字节,因此需要重复调用 write()方法," +
"直到 Buffer 中已经没有尚未写入通道的字节。").getBytes());
System.out.println(buffer.toString());
//切换为读模式,切换后可以读取buffer中的数据
// H T T P / 1 ... ! _ _
// P(L)
// P L
buffer.flip();
System.out.println(buffer.toString());
while (buffer.hasRemaining()) { //当position==limit时,说明已经把buffer中的数据读完了
channel.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//用完 FileChannel 后必须将其关闭。如: inChannel.close();
if (channel != null) {
try {
channel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
void transferFrom() {
RandomAccessFile readFile = null;
RandomAccessFile writeFile = null;
try {
readFile = new RandomAccessFile("src/main/resources/read.text", "rw");
writeFile = new RandomAccessFile("src/main/resources/write.text", "rw");
FileChannel fromChannel = readFile.getChannel();
FileChannel toChannel = writeFile.getChannel();
//设置起始/结束位置
long position = 0;
long count = fromChannel.size();
//long l = toChannel.transferFrom(fromChannel, position, count);
long l = fromChannel.transferTo(position, count, toChannel);
System.out.println(l);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (readFile != null) {
readFile.close();
}
if (writeFile != null) {
writeFile.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ServerSocketChannel
- nio服务端
- ServerSocketChannel 是一个基于通道的 socket 监听器(本身不传数据,而是一个监听器)。
- 由于 ServerSocketChannel 没有 bind()方法,因此有必要取出对等的 socket 并使用它来绑定到一个端口以开始监听连接。我们也是使用对等 ServerSocket 的 API 来根据需要设置其他的 socket 选项。
package com.yangmin.study.io.nio.channel;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class ServerSocketChannelTest {
public static void main(String[] args) {
ServerSocketChannelTest serverSocketChannelTest = new ServerSocketChannelTest();
serverSocketChannelTest.test1();
}
void test1() {
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(8888));
serverSocketChannel.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
for (; ; ) {
//我们这里采用的是非阻塞模式
SocketChannel socketChannel = serverSocketChannel.accept();
if (socketChannel == null) {
System.out.println("null");
Thread.sleep(2000);
continue;
}
socketChannel.read(buffer);
System.out.println("this is a data from client, "+new String(buffer.array()));
//重置position=0,进行写操作
buffer.rewind();
buffer.put("hello client, i am server".getBytes());
//切换为读操作
buffer.flip();
socketChannel.write(buffer);
buffer.clear();
socketChannel.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
SocketChannel
nio客户端
1、SocketChannel 介绍
Java NIO 中的 SocketChannel 是一个连接到 TCP 网络套接字的通道。操作面向Buffer缓冲区
A selectable channel for stream-oriented connecting sockets,这是 Java docs 中对于 SocketChannel 的描述:SocketChannel 是一种面向流连接sockets 套接字的可选择通道。从这里可以看出:
- SocketChannel 是用来连接 Socket 套接字
- SocketChannel 主要用途用来处理网络 I/O 的通道
- SocketChannel 是基于 TCP 连接传输
- SocketChannel 实现了可选择通道,可以被多路复用的
- 用于TCP的网络I/O套接字的通道
2、SocketChannel 特征:
(1)对于已经存在的 socket 不能创建 SocketChannel
(2)SocketChannel 中提供的 open 接口创建的 Channel 并没有进行网络级联,需要使用 connect 接口连接到指定地址
(3)未进行连接的 SocketChannle 执行 I/O 操作时,会抛出NotYetConnectedException
(4)SocketChannel 支持两种 I/O 模式:阻塞式和非阻塞式
(5)SocketChannel 支持异步关闭。如果 SocketChannel 在一个线程上 read 阻塞,另一个线程对该 SocketChannel 调用 shutdownInput,则读阻塞的线程将返回-1 表示没有读取任何数据;如果 SocketChannel 在一个线程上 write 阻塞,另一个线程对该SocketChannel 调用 shutdownWrite,则写阻塞的线程将抛出AsynchronousCloseException
(6)SocketChannel 支持设定参数
SO_SNDBUF 套接字发送缓冲区大小
SO_RCVBUF 套接字接收缓冲区大小
SO_KEEPALIVE 保活连接
O_REUSEADDR 复用地址
SO_LINGER 有数据传输时延缓关闭 Channel (只有在非阻塞模式下有用)
TCP_NODELAY 禁用 Nagle 算法
package com.yangmin.study.io.nio.channel;
import jdk.nashorn.internal.ir.Block;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
public class SocketChannelTest {
public static void main(String[] args) {
SocketChannelTest socketChannelTest = new SocketChannelTest();
// socketChannelTest.unBlock();
socketChannelTest.block();
}
void block(){
System.out.println("connect start");
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
//SocketChannel.open(new InetSocketAddress("127.0.0.1",8888));
if (socketChannel.isConnected()){
System.out.println("connect server success");
}
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("hello serverrn".getBytes());
buffer.flip();
socketChannel.write(buffer);
System.out.println("send data to server");
buffer.clear();
socketChannel.read(buffer);
System.out.println("accept data from server :"+new String(buffer.array()));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (socketChannel!=null){
try {
socketChannel.close();
System.out.println("aaaaaaaa");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
void unBlock(){
System.out.println("connect start");
SocketChannel socketChannel = null;
try {
socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("127.0.0.1",8888));
//设置 SocketChannel 的读写模式。false 表示非阻塞,true 表示阻塞
socketChannel.configureBlocking(false);
if (socketChannel.isConnected()){
System.out.println("connect server success");
}
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put("hello serverrn".getBytes());
buffer.flip();
socketChannel.write(buffer);
System.out.println("send data to server");
buffer.clear();
socketChannel.read(buffer);
System.out.println("accept data from server :"+new String(buffer.array()));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socketChannel!=null){
try {
socketChannel.close();
System.out.println("aaaaaaaa");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
DatagramChannel
发包
package com.yangmin.study.io.nio.channel;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
public class DatagramChannelSendTest {
public static void main(String[] args) {
DatagramChannelSendTest datagramChannelTest = new DatagramChannelSendTest();
datagramChannelTest.sendData();
}
private void sendData() {
try {
DatagramChannel datagramChannel = DatagramChannel.open();
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 8888);
datagramChannel.send(ByteBuffer.wrap("发包".getBytes()), inetSocketAddress);
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
收包
package com.yangmin.study.io.nio.channel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.DatagramChannel;
public class DatagramChannelAcceptTest {
public static void main(String[] args) throws IOException {
DatagramChannel server = DatagramChannel.open();
InetSocketAddress socketAddress = new InetSocketAddress(8888);
server.bind(socketAddress);
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true){
buffer.clear();
server.receive(buffer);
System.out.println(new String(buffer.array()));
System.out.println("发来信息的ip地址:"+socketAddress.toString());
}
}
}



