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 是有点类似的,但又有些不同:
Channel是一个接口,Java比较常见的几个实现类:
FileChannel:
我们不能直接获取FileChannel对象,可以通过以下获得:
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();
https://docs.oracle.com/javase/8/docs/api/java/nio/channels/DatagramChannel.html
通过调用open此类的方法之一来创建数据报通道。无法为任意现有的数据报套接字创建通道。新建的数据报通道已打开但未连接。无需连接数据报通道即可使用send和receive方法。可以通过调用数据报通道的connect方法来连接数据报通道,以避免安全检查的开销,否则将作为每个发送和接收操作的一部分执行安全检查。为了使用read和write方法,必须连接一个数据报通道,因为这些方法不接受也不返回套接字地址。
连接后,数据报通道将保持连接状态,直到断开连接或关闭为止。可以通过调用数据报信道的isConnected方法来确定是否连接了数据报信道。
重要方法:
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
定义:套接字通道是一个可选类型的通道,面向流的数据流连接套接字。
通过调用其静态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();