通道:channel用于字节缓冲区和位于通道另一侧的实体(通常是文件或者套接字)之间有效的传输数据。
两种类型的通道:1、FileChannel类(他总是一种阻塞式的)和
2、socket通道类:SocketChannel,ServerSocketChannel,DatagramSocket;
Socket通道有可以直接创建socket通道的工厂方法,FileChannel对象只能在一个打开的RandomAccessFile,FileInputStream,或者FileOutputStream对象上调用getChannel()方法获取,不能直接创建FileChannel对象。
通道使用,通道有单向和双向的,实现ReadableByteChannel和WriteableByteChannel.其中之一的是单向通道,两者都实现了是双向通道即可读又可写。实现读的通道只能用read()方法,用write方法报错哪怕类中有这个方法。实现写的同理。也就是说:通道会连接一个特定I/O服务且通道实例的性能会受到他所链接的I/O服务特征限制。
文件通道
FileChannel是线程安全的,多个线程可以在同一个实例上并发调用方法不会引起任何问题,但是影响通道位置和文件大小的操作是单线程的。
访问文件:read和write,文件位置position,自动随着读写更新位置。读到文件末尾是返回-1,写入文件末尾,也即是写的时候超出文件大小(通过size()方法获取)是,该文件会扩展以容纳新的字节(这一点和缓冲区不同)。
Socket通道
全部socket通道类(java.net....以上三种)在被实例化时都会创建一个对等socket对象。对等socket对象可以通过调用socket()方法从一个通道中获取,这三个类都有一个getchannel()方法。虽然每个socket通道都有关联一个socket对象,但并非所有socket对象都关联一个socket通道。传统方式(直接实例化)创建的socket对象就不会关联Socket通道,调用他的getchannel()方法返回为null。
通道的阻塞模式可以设置通过configureBlocking(boolean)方法设置,传入true是阻塞模式,false是非阻塞模式,isBlocking()检查是否阻塞(服务端一般用非阻塞模式)
ServerSocketChannel:
是一个基于通道的socket监听器,用静态的open工厂方法创建一个serverSocketChannel对象,将会返回同一个未绑定的java.net.ServerSocket关联的通道,该serverSocket可以通过在返回的serverSocketChannel中调用socket()方法,这些serverSocket对象依赖于通道的实现。通道一般不能被随意的socket的对象外面,但是serversocketchannel没有bind()方法,因此有必要取出对等的socket来绑定到一个端口一开始监听链接,也可以通过serversocket的API来根据需要设置socket的其他选项。
和java.net.serversocket一样,serverSocketchannel也有accept()方法,一旦创建了serversocketchannel并用对等的socket绑定了它,然后就可以调用其中的socket方法。
在serversocket上调用accept方法,和在其他serversocket上一样:总是阻塞并返回一个java.net.socket对象,在Serversocketchannel上调用accept方法,返回socketchannel对象,返回的对象是在非阻塞模式下运行,假如系统有一个安全管理器,则两种形式的方法执行相同的安全检查。
如果以非阻塞模式下被调用,当没有传入链接在等待时,serversocketchannel.accept()方法会立即返回一个null,这链接不阻塞实现可伸缩性和降低复杂度。可以使用一个选择器实例来注册serversocketchannel对象以实现新连接到达是自动通知。
Socketchannel:
Socketchannel封装了点对点,有序网络链接,Socketchannel扮演客户端发送同一个监听服务器的链接,知道链接成功,他只能从链接成功的地址接受数据。静态的open方法会创建一个socketchannel对象,在该对象上调用socket返回一个socket,在该socket上调用getchannel则返回该socketchannel对象。
新创建出的socketchannel虽打开但没有链接。调用读写方法抛出NotYetConnectException异常.直接调用connect方法(阻塞直到链接成功,(并没有制定超时的设置)通道默认是阻塞的)或者关联的socket上调用connect方法链接(也就是传统的模式调用connect,阻塞或者超时,直到连接成功)两个链接过程一致。
当connect在非阻塞模式下调用时,socketchannel提供并发链接,发起对请求地址的链接,并且立即返回值。True为成功,false为失败,如果链接不能成功则并发地继续链接建立过程。
在一个非阻塞模式下调用finishChannel()会出现下列情形之一:
1、connect()方法尚未被调用,那么将产生NoConnectionPendingException异常。
2、连接过程正在进行尚未完成,那么什么都没有发生,finishchannel()方法直接返回false。
3、在非阻塞模式下调用connect()之后,socketchannel又被切换了阻塞模式,如果有必要的话调用线程会阻塞直到连接简历完成finishchannel()方法接着返回true,
4、在初次调用connect()或者最后一次调用finnishConnect()之后,连接建立过程已经完成,那么socketchannel对象的内部状态将被更新到已连接状态,finnishchannel()调用会返回true值,然后socketChannel对象就可以被用来传输数据。
5、连接已经建立完成,那么什么都不会发生,finiashchannel()直接返回true。
Socket通道案例:
服务端:package it.com.Jerome;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
public class NIOSocketChannelServer {
private ByteBuffer buff=ByteBuffer.allocate(1024);
private IntBuffer intBuff = buff.asIntBuffer();
private SocketChannel clientChannel=null;
private ServerSocketChannel serverChannel =null;
//打开服务通道;
public void openChannel() throws Exception{
serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(8989));
System.out.println("服务器通道已经打开!");
}
//等待客户端请求链接;
public void waitReqConn() throws Exception{
while(true){
clientChannel=serverChannel.accept();
if(clientChannel!=null){
System.out.println("新的链接已加入!");
}
processReq();//处理请求
clientChannel.close();
}
}
public void processReq() throws Exception {
System.out.println("开始读取和处理客户端数据!");
buff.clear();//当前位置为0,上限值为容量的值。
clientChannel.read(buff);
int result = intBuff.get(0)+intBuff.get(1);
System.out.println(result);
buff.flip();
buff.clear();
intBuff.put(0, result);
clientChannel.write(buff);
System.out.println("客户端数据读取和处理完成!");
}
public void start(){
try{
openChannel();
waitReqConn();
clientChannel.close();
System.out.println("服务端处理完成");
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
new NIOSocketChannelServer().start();
}
}
客户端:
package it.com.Jerome;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.nio.channels.SocketChannel;
public class NIOSocketChannelClient {
private SocketChannel channel=null;
private ByteBuffer buff=ByteBuffer.allocate(8);
private IntBuffer intBuff = buff.asIntBuffer();
public SocketChannel connect() throws Exception{
return SocketChannel.open(new InetSocketAddress("127.0.0.1",8989));
}
public void sendRequest(int a,int b) throws Exception{
buff.clear();
intBuff.put(0, a);
intBuff.put(1, b);
channel.write(buff);
System.out.println("发送请求!");
}
public int receiveRequest() throws Exception{
buff.clear();
channel.read(buff);
return intBuff.get(0);
}
public int getSum(int a,int b){
int result=0;
try{
channel = connect();
sendRequest(a,b);
result=receiveRequest();
}catch(Exception e){
e.printStackTrace();
}
return result;
}
public static void main(String[] args) {
int result = new NIOSocketChannelClient().getSum(45, 34);
System.out.println("处理结果完成!"+result);
}
}
输出是79;
文件通道案例:package it.com.Jerome;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileSocketChannel {
public static void main(String[] args) {
fileChannelDemo();
}
private static void fileChannelDemo() {
try{
ByteBuffer buff=ByteBuffer.allocate(1024);
FileChannel inFc=new FileInputStream(
"C:/helloworld1.txt").getChannel();
FileChannel outFc=new FileOutputStream(
"C:/helloworld2.txt",true).getChannel();
buff.clear();
int len=inFc.read(buff);
System.out.println(new String(buff.array(),0,len));
ByteBuffer buff2=ByteBuffer.wrap("jack".getBytes());
outFc.write(buff2);
inFc.close();
outFc.close();
}catch(Exception e){
e.printStackTrace();
}
}
}