德州做名片的网站,连江网站建设,网络广告设计案例,四川发布干部任前公示1 概述 前面提到#xff0c;Java NIO中一个socket连接使用一个Channel来表示。从更广泛的层面来说#xff0c;一个通道可以表示一个底层的文件描述符#xff0c;例如硬件设备、文件、网络连接等。然而#xff0c;远不止如此#xff0c;Java NIO的通道可以更加细化。例如Java NIO中一个socket连接使用一个Channel来表示。从更广泛的层面来说一个通道可以表示一个底层的文件描述符例如硬件设备、文件、网络连接等。然而远不止如此Java NIO的通道可以更加细化。例如不同的网络传输协议在Java中都有不同的NIO Channel实现。 这里不对Java NIO的全部通道类型进行过多描述仅着重介绍其中最为重要的四种Channle实现FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel。 对于以上四种通道说明如下 1FileChannel文件通道用于文件的数据读写。 2SocketChannel套接字通道用于套接字TCP连接的数据读写。 3ServerSocketChannel服务器套接字通道或服务器监听通道允许监听TCP连接请求位每个监听的请求创建一个SocketChannel通道。 4DatagramChannel数据报通道用于UDP的数据读写。 这四种通道涵盖了文件IO、TCP网络、UDP IO三类基础IO读写操作。
2 FileChannel FileChannel是专门操作文件的通道。通过FileChannel既可以从一个文件中读取数据也可以把数据写入文件中。特别申明一下FileChannel位阻塞模式不能设置为非阻塞模式。 下面分别介绍FileChannel的获取、读取、写入、关闭四个操作。 2.1 获取FileChannel public class Test_FileChannel {final static String srcFile /Users/jay/Documents/files/nio.txt;final static String outFile /Users/jay/Documents/files/nio1.txt;public static void main(String[] args) throws IOException {//创建一个文件输入流FileInputStream fis new FileInputStream(srcFile);//获取文件流的通道FileChannel inChannel fis.getChannel();//创建一个文件输出流FileOutputStream fos new FileOutputStream(outFile);//获取文件流的通道FileChannel outChannel fos.getChannel();System.out.println(outChannel.size());}
} 2.2 读取FileChannle 在大部分引应用场景中从通道读取数据都会调用通道的int read(ByteBuffer buf)方法它把从通道读取的数据写入 ByteBuffer缓冲区并且返回读取的数据量。
public class Test_FileChannel {final static String srcFile /Users/jay/Documents/files/nio.txt;final static String outFile /Users/jay/Documents/files/nio1.txt;public static void main(String[] args) throws IOException {RandomAccessFile rw new RandomAccessFile(srcFile, rw);//获取通道可读可写FileChannel channel rw.getChannel();//获取一个字节缓冲区ByteBuffer buffer ByteBuffer.allocate(30);int length -1;//调用通道read方法读取数据并写入字节类型的缓冲区while((length channel.read(buffer)) ! -1){System.out.println(length);}}
} 2.3 写入FileChannel 把数据写入通道在大部分引用场景中都会调用通道的write(ByteBuffer buf)方法此方法的参数是一个ByteBuffer缓冲区示例是待写数据的来源。 write(ByteBuffer buf)方法的作用是从ByteBuffer缓冲区中读取数据然后写入通道自身而返回值是写入成功的字节数。
public class Test_FileChannel {final static String srcFile /Users/jay/Documents/files/nio.txt;final static String outFile /Users/jay/Documents/files/nio1.txt;public static void main(String[] args) throws IOException {RandomAccessFile rw new RandomAccessFile(srcFile, rw);//获取通道可读可写FileChannel channel rw.getChannel();//获取一个字节缓冲区ByteBuffer buffer ByteBuffer.allocate(30);int length -1;//调用通道read方法读取数据并写入字节类型的缓冲区while((length channel.read(buffer)) ! -1){System.out.println(length);}buffer.flip();int outlength 0;while((outlength channel.write(buffer)) ! 0){System.out.println(写入的字节数 outlength);}}
} 2.4 关闭通道 当通道使用完后必须将其关闭。调用close()方法 .
public class Test_FileChannel {final static String srcFile /Users/jay/Documents/files/nio.txt;final static String outFile /Users/jay/Documents/files/nio1.txt;public static void main(String[] args) throws IOException {RandomAccessFile rw null;FileChannel channel null;try {rw new RandomAccessFile(srcFile, rw);//获取通道可读可写channel rw.getChannel();//获取一个字节缓冲区ByteBuffer buffer ByteBuffer.allocate(30);int length -1;//调用通道read方法读取数据并写入字节类型的缓冲区while((length channel.read(buffer)) ! -1){System.out.println(length);}buffer.flip();int outlength 0;while((outlength channel.write(buffer)) ! 0){System.out.println(写入的字节数 outlength);}}catch (Exception e){e.printStackTrace();}finally {channel.close();}}
}
2.5 强制刷新到磁盘 在将缓冲区写入通道时出于性能的原因操作系统不可能每次都实时地将写入数据保存到硬盘完成最终的数据保存。在将缓冲区数据写入通道时要保证数据能写入硬盘可以在写入后调用FileChannel类的force()方法。channel.force(true)
2.6 使用FileChannle 完成文件发复制实战
需求使用FileChannel复制文件把原文件中的内容复制到目标文件中。
public class FileNIOCopyDemo {//原文件路径public static final String srcPath ;//目标文件路径public static final String destPath ;public static void main(String[] args) {//演示复制资源文件nioCopyFile(srcPath,destPath);}public static void nioCopyFile(String srcPath, String destPath){File src_file new File(srcPath);File dest_file new File(destPath);try {//如果目标文件不存在则新建if(!dest_file.exists()){dest_file.createNewFile();}long start_time System.currentTimeMillis();FileInputStream fis null;FileOutputStream fos null;//输入通道FileChannel inChannel null;//输出通道FileChannel outChannel null;try {fis new FileInputStream(src_file);fos new FileOutputStream(dest_file);inChannel fis.getChannel();outChannel fos.getChannel();int length -1;//新建buf处于写模式ByteBuffer buffer ByteBuffer.allocate(1024);//从输入通道读取bufferwhile((length inChannel.read(buffer)) ! -1){//buffer第一次切换成功从写模式变成读模式buffer.flip();int outlength 0;//把buffer写入输出的通道while ((outlength outChannel.write(buffer)) ! 0){System.out.println(写入的字节数 outlength);}//buffer第二次模式切花清除buffer变成写模式buffer.flip();}//强制刷新到磁盘outChannel.force(true);}finally {outChannel.close();fos.close();inChannel.close();fis.close();}long end_time System.currentTimeMillis();System.out.println(复制花费的时间是 (end_time - start_time) 毫秒);}catch (Exception e){}}
}
3 SocketChannel 3.1 概述 在NIO中涉及网路连接的通道有两个一个是SocketChannel负责连接的数据传输另一个是ServerSocketChannel负责连接的监听。其中NIO中的SocketChannel传输通道与OIO中的Socket类对应NIO中的ServerSocketChannel监听通道对应于OIO中的ServerSocket类。 ServerSocketChannel仅应用于服务端而 SocketChannel 同时处于服务端和客户端。所以对于一个链接两端都有一个负责传输的SocketChannel。 无论是ServerSocketChannel 还是 SocketChannel都支持阻塞和非阻塞两种模式。如何进行模式的设置呢调用configureBlocking()方法具体如下 1socketChannel.configureBlocking(false)设置为非阻塞模式。 2socketChannel.configureBlocking(true)设置为阻塞模式。 在阻塞模式下SocketChannel的连接、读写操作都是同步阻塞式的在效率上与Java OIO面向流的阻塞式读写操作相同。因此在这里不介绍阻塞模式下通道的具体操作。在非阻塞模式下通道的操作是异步、高效的这也是相对于传统的OIO的优势所在。 3.2 获取SocketChannel传输通道
【重要通知本章小节里的代码只是方法演示不具备运行功能。在介绍完SocketChannel常用的方法后会举例并成功运行】 在客户端先通过SocketChannel静态方法open()获得一个套接字传输通道然后将socket设置为非阻塞模式最后通过connet()实例方法对服务器的IP和端口发起连接。在非阻塞情况下与服务器的连接可能还没有建立socketChannel.connect()方法就反悔了因此需要不断地自旋检查当前是否连接到主机。
客户端ClientTest.java
public class ClientTest {public static void main(String[] args) {//获取一个套接字传输通道try {SocketChannel socketChannel SocketChannel.open();socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress(127.0.0.1,80));while(! socketChannel.finishConnect()){System.out.println(连接成功);}} catch (IOException e) {throw new RuntimeException(e);}}
} 在连接建立的事件到来时服务端的ServerSocketChannel能成功地查询出这个新连接事件并且通过调用服务端ServerSocketChannel监听套接字的accept()方法来获取新连接的套接字通道。
服务端ServerTest.java
public class ServerTest {public static void main(String[] args) throws IOException {//新连接事件到来首先通过事件获取服务器监听通道//key是具体的什么事件是否为新连接事件-后面会说在这里理解为一个事件对象ServerSocketChannel server (ServerSocketChannel) key.channel();//获取新连接的套接字通道SocketChannel socketChannel server.accept();//设置为非阻塞模式socketChannel.configureBlocking(false);}
}
【说明】NIO套接字通道主要用于非阻塞的传输场景。
3.3 读取SocketChannel传输通道 当SocketChannel传输通道可读时可以从SocketChannel读取数据具体方法与前面的文件通道读取方法时相同的。调用read()方法将数据读入缓冲区ByteBuffer。
ByteBuffer buffer ByteBuffer.allocate(1024);
int byteRead socketChannel.read(buffer); 在读取时因为是异步的所以我们必须检查read()的返回值以便判断当前是否读取了数据。read()方法的返回值是读取的字节数如果是-1那么表示读取到对方的输出结束标志即对方已经输出结束准备关键连接。实际上通过read()方法读数据本身是很简单的比较困难的是在非阻塞模式下如何知道通道何时是可读的。这需要用到NIO的新组件——Selector通道选择器稍后会介绍它。
3.4 写入SocketChannel传输通道 和前面把数据写入FileChannel一样大部分应用场景都会调用通道的write(ByteBuffer buf)方法。
//写入缓冲区前要求ByteBuffer是读模式
buffer.flip();
socketChannel.write(buffer);
3.5 关闭SocketChannel传输通道 在关闭SocketChannel传输通道前如果传输通道用来写入数据则建议调用一次shutdownOutput()终止输出方法向对方发送一个输出的结束标志(-1)然后调用socketChannel.close()方法关闭套接字连接。
SocketChannel socketChannel SocketChannel.open();
socketChannel.shutdownOutput();
socketChannel.close();
3.6 使用 SocketChannel 发送文件 实战 需求使用FileChannel读取本地文件内容然后在客户端使用SocketChannel 把文件信息和文件内容发送到服务器。
public class NioSendClient {private Charset charset Charset.forName(UTF-8);public static final String SMALL_PATH /Users/jay/Documents/files/src_nio.txt;public static final String BIG_PATH /Users/jay/Documents/files/孔乙己.docx;//向服务器传输文件public void sendFile() throws Exception {File file new File(BIG_PATH);FileChannel fileChannel new FileInputStream(file).getChannel();SocketChannel socketChannel SocketChannel.open();socketChannel.socket().connect(new InetSocketAddress(127.0.0.1,18899));socketChannel.configureBlocking(false);System.out.println(成功连接服务端);while(!socketChannel.finishConnect()){int count 0;//不断自旋、等待或者做一些其他的是System.out.println(count );}//发送文件的名称ByteBuffer fileNameByteBuffer charset.encode(file.getName());ByteBuffer buffer ByteBuffer.allocate(1024);//发送的文件长度int fileNameLen fileNameByteBuffer.remaining();buffer.clear();buffer.putInt(fileNameLen);//切换到读模式buffer.flip();socketChannel.write(buffer);System.out.println(Client 文件名称长度发送完成fileNameByteBuffer);//发送文件socketChannel.write(fileNameByteBuffer);System.out.println(Client 文件名称名称发送完成 file.getName());//清空buffer.clear();buffer.putInt((int) file.length());//切换到读模式buffer.flip();//写入文件长度socketChannel.write(buffer);System.out.println(Client 文件长度发送完成: file.length());//发送文件内容System.out.println(开始传输文件);int length 0;long offset 0L;buffer.clear();while((length fileChannel.read(buffer)) 0){buffer.flip();socketChannel.write(buffer);offset length;System.out.println(| (100 * offset / file.length()) % |);buffer.clear();}//等待一分钟关闭连接Thread.sleep(60000);if (length -1){fileChannel.close();socketChannel.shutdownOutput();socketChannel.close();}System.out.println(文件传输成功);}}