Java nio

来源:互联网 发布:船舶节能软件供应商 编辑:程序博客网 时间:2024/05/21 02:52

Java nio(一)

jdk1.4提供了java.nio包,为从根本上改善I/O的性能提供了可能,但是nio要比以前的I/O要复杂,提供了更底层的操作和更细的api。学起来并不是那么快就上手,有专门一本书
介绍nio的。我希望通过总结更好的梳理整个nio框架各个类之间的关系,从而能够灵活的使用nio包。
nio通常需要涉及到三个对象:
1、数据源:从文件中获得的FileInputStream/FileOutputStream、从socket中获得的输入输出流等
2、缓冲区对象(buffer):nio定义了一系列buffer对象,最基本的是ByteBuffer,其他还有各种基本数据类型除了boolean类型外,所对应的buffer类型。使用这些buffer一般的过程是:从数据源中读到ByteBuffer中,然后使用ByteBuffer.asXxxBuffer()转换成特定的基本类型,然后对这个基本类型的buffer进行操作;把基本类型转换成ByteBuffer类型,然后写入数据源中。
3、通道对象(channel):他提供了对数据源的一个连接,可以从文件中获得FileChannel,从Socket中获得SocketChannel,它是一个中介。提供了数据源与缓冲区之间的读写操作。
我们可以同过一句话来解释他们三者之间的关系:
channel对象提供了数据源与缓冲区之间的读写操作,是数据源与缓冲区的一个桥梁,通过这个桥梁,数据在两者之间流动。
首先我们看看这个中介Channel类的整个结构:

Java代码 复制代码
  1. Channel < Closable   
  2. WritableByteChannel < Channel   
  3. InterruptibleChannel < Channel   
  4. ReadableByteChannel < Channel   
  5. GetherByteChannel < WritableByteChannel    
  6. ByteChannel < WritableByteChannel,ReadableByteChannel    
  7. ScatteringByteChannel < ReadableByteChannel   


Channel接口声明了两个方法:
close(),用来关闭通道
isOpen(),用于测试通道是否打开
其他的通道:
ByteChannel只是简单的合并了WritableByteChannel和ReadableChannel的,ScatteringByteChannel接口扩展了ReadableChannel接口,添加了一个读取并将数据
分布在不同缓冲区的方法,而GatheringByteChannel接口在WritableByteChannel的基础上添加了将多个独立的缓冲区写入驱动器中的方法。
Java代码 复制代码
  1. Closeable:   
  2. void close()//关闭源或目的地,并释放资源   
  3. Channel:   
  4. void close()//关闭通道   
  5. void boolean isOpen()//判断通道是否打开   
  6. ReadableByteChannel:   
  7. int read(ByteBuffer input)   
  8. //将字节从通道读入input缓冲区中,返回读取的字节数,到达流结尾时,返回-1   
  9. WritableByteChannel:   
  10. int write(ByteBuffer output)   
  11. //将字节从实参output缓冲区写入通道,返回写的字节数   
  12. ByteChannel 仅仅继承了ReadableByteChannel和WritableByteChannel的方法   
  13. ScatteringByteChannel:   
  14. int read(ByteBuffer[] inputs)   
  15. //将字节从通道读入inputs缓冲区数组中,返回读取的字节,到达流末尾是,返回-1   
  16. int read(ByteBuffer[] inputs,int offset,int length)   
  17. //将字节从通道读inputs[offset]---inputs[offeset+length-1]缓冲区中   
  18. GatherByteChannel:   
  19. int write(ByteBuffer[] outputs);   
  20. //把outputs缓存区数组写入通道中   
  21. int write(ByteBuffer[] outputs,int offset,int length);   
  22. 把从outputs[offset]-->outputs[offset+length-1]缓冲区写入通道中  

例子:(我们忽略了各种Exception)
Java代码 复制代码
  1. File file = new File("C://test.txt");   
  2. FileOutputStream fos = new FileOutputStream(file);   
  3. FileChannel outputChannel = fos.getChannel();   
  4. String str = "Hello,just a test";   
  5. ByteBuffer bb = ByteBuffer.wrap(str.getBytes());   
  6. outputChannel.write(bb);  

 

Java nio(二)

 

 

一、各种缓冲区:
所有的缓冲区都继承了Buffer,Buffer类定义了所有缓冲区共有的基本特征,缓冲区存储了制定类型的元素序列有:ByteBuffer、CharBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBuffer、DoubleBuffer。
我们需要知道这些基本类型占用存储空间与Byte的对应关系:
char 2 bytes,int 4 bytes,double 8 bytes
二、我们看看Buffer三个重要的概念以及他们之间的关系:
capacity 所能存方特定类型值得最大数量
limit 第一个不能读取或写入的索引位置
position 下一个要读取或写入的缓冲区索引位置
理解这三者的关系是对缓冲区操作的关键:
新创建的一个缓冲区,position=0,limit=capacity
我们可以通过position(int newPosition)和limit(int newLimit)来调整position和limit的值。显然0 =< position =< limit =< capacity,所以我们在设置这个位置
需要小心,以满足这个条件,一般如果同时设置limit和position的时候,下面代码是一个
安全的方法:

Java代码 复制代码
  1. buf.position(0).limit(newLimit).position(newPosition);  


position和capacity存在的理由不需要解释。


为什么需要limit呢?这是理解缓冲区操作的关键所在。
一段数据怎么去标示出来呢?两个点决定了一个段啊,
而position就是起点,limit就是终点。但这个起点和终点在不同时候,他
表示的含义不同:
当把数据写入buffer时,poision到limit就是可以写的空间
当从buffer读数据时,position到limit就是可读取的数据
所以我们经常做的事情就是:
把数据写入Buffer,,然后使用buf.flip()操作,最后把Buffer写入File中。这个过程发生了什么?
我们把数据写入Buffer后,position为写入的最后一个数据的位置,而buf.flip()是
buf.limit(buf.position()).position(0);的简写形式。这样position到limit就
标示了可以写到文件的数据


另外还有一个mark()方法,他记录了上一次position和limit的位置,通过reset()可以
恢复到这个状态。
三、下面我们看看如何创建缓冲区的:
缓冲区类没有共有的构造方法,但提供了静态工厂方法allocate来创造缓冲区

Java代码 复制代码
  1. ByteBuffer buf = ByteBuffer.allocate(1024);   
  2. FloatBuffer buf = FloatBuffer.allocate(100);   
  3. //...  


我们可以通过wrap来把字节数组包装成ByteBuffer

Java代码 复制代码
  1. String str = "Hi,just a test";   
  2. byte[] arr = str.getBytes();   
  3. ByteBuffer buf = ByteBuffer.wrap(arr);   
  4. //ByteBuffer buf = ByteBuffer.wrap(arr,offset,len);  


四、视图缓存区:
从名字我们可以知道这种缓冲区是为了提供方便操作的视图而存在的,从ByteBuffer可以
得到各种基本类型(boolean除外)对应的缓冲区,这个过程是通过asXxBuffer()方法得到的。
有了视图缓冲区我们就可以方便的对缓冲区进行操作了,所做的操作直接影响后备缓冲区(ByteBuffer)的数据,但不影响后备缓冲区的limit和position.所以我们如果这时候
在想把后备缓冲区写入文件的时候,需要手工调整limit值。
五、缓冲区数据的传送:
1、将数据送到缓冲区:
ByteBuffer有两种put操作(相对的和决对的),用来传送数据到缓冲区:

Java代码 复制代码
  1. put(byte b);   
  2. put(int index,byte b);   
  3. put(byte[] array,int offset,int len);   
  4. put(ByteBuffer src);  


例子:

Java代码 复制代码
  1. String text = "Value of e";   
  2. ByteBuffer buf = ByteBuffer.allocate(text.length() + sizeof(Math.E));   
  3. buf.put(text.getBytes()).putDouble(Math.E);  


这个例子会用本地的编码获的字节数组。
而如果要想以原来Unicode字符码的形式传递的话,可以这么写:

Java代码 复制代码
  1. char[] array = text.toCharArray();   
  2. ByteBuffer buf = ByteBuffer.allocate(50);   
  3. for(char ch : array)   
  4.   buf.putChar(ch);   
  5. buf.putDouble(Math.E);  


六、写文件
我们把数据写入缓冲区和上次我们总结的用Channel把缓冲区的数据写入文件合起来就可以写文件了:

Java代码 复制代码
  1. String text = "Value of e";   
  2. ByteBuffer buf = ByteBuffer.allocate(text.length() + sizeof(Math.E));   
  3. buf.put(text.getBytes()).putDouble(Math.E);   
  4. buf.flip();   
  5. File file = new File("D:/test.txt");   
  6. FileOutputStream fos = new FileOutputStream(file);   
  7. FileChannle outputChannel = fos.getChannel();   
  8. outputChannel.write(buf);   
  9. fos.close();  


六、使用视图缓冲区:
前面我们介绍了视图缓冲区,我们看看使用视图缓冲区写文件的例子:

Java代码 复制代码
  1. String greeting = "Hi,nice to meet you!";   
  2. ByteBuffer buf = ByteBuffer.allocate(1024);   
  3. CharBuffer charBuf = buf.asCharBuffer();//得到试图缓冲区   
  4. charBuf.put(greeting);//把字符串转换成Buffer,操作视图缓冲区   
  5. buf.limit(2*charBuffer.position());   
  6. FileChannel outputChannel = fos.getChannel();   
  7. fos.write(buf);   
  8. fos.close();  


七、集中写操作:
文件通道有两个方法可以执行集中写操作:
write(ByteBuffer[] buffers);
write(ByteBuffer[] buffers,int offset,int length);
把分散在多个buffer的数据集中写入文件
我们看一个例子:

Java代码 复制代码
  1. String str = "hi,just a test";   
  2. dobule d = Math.E;   
  3. FileChannel outputChannel = fos.getChannel();   
  4. ByteBuffer[] buffers = new ByteBuffer[2];   
  5. buffer[0] = ByteBuffer.allocate(str.length());   
  6. buffer[1] = ByteBuffer.allocate(sizeof(i));   
  7. buffer[0].put(str.getBytes).flip();   
  8. buffer[1].putDouble(d);   
  9. outputChannel.write(buffers);   
  10. fos.close();  

 

 

Java nio(三)

关键字: java nio

一、读文件:
前面我们介绍了通过获得FileChannel来写文件,现在我们再看看读操作。
其实读操作和写操作基本是对应的。
首先与写操作类似从文件中获得流,读操作要获得输入流FileInputStream
,再从这个流中得到FileChannel对象,然后进行读操作。
文件通道的读操作有三个read()方法:
Java代码 复制代码
  1. int read(ByteBuffer buf);   
  2. 从文件中读取buf.remaining()[即limit-position]个字节到缓冲区buf,从缓冲区的当前位置开始存取   
  3. int read(ByteBuffer[] buffers);   
  4. 从文件中读到多个缓冲区中   
  5. int read(ByteBuffer[] buffers,int offset,int length);   
  6. 从文件中读到buffers[offset]--buffers[offset+length-1]缓冲区中  

用个小例子描述上面的过程:
Java代码 复制代码
  1. File file = new File("D:/text.txt");   
  2. FileInputStream fis = new FileInputStream(file);   
  3. FileChannel inputChannel = fis.getChannel();   
  4. ByteBuffer buf = ByteBuffer.allocate(48);   
  5. while(inChannel.read(buf) != -1){   
  6.   //....从buf中提取出数据;for example:   
  7.   System.out.println(buf.filp().asCharBuffer().toString());   
  8.   buf.clear();   
  9. }  

二、文件复制:
文件通道定义了两个方法,可以进行直接的文件传输:
int transferTo(int position,long count,WritableByteChannel dst);
这个函数是把文件从position位置开始向dst通道传送count个字节。
int transferFrom(ReadableByteChannel src,long position,long count);
将count个字节从通道src传送到文件中,position是为文件开始写入的位置。
从FileInputStream中获得的通道只支持transferTo,而从FileOutputStream中获得
的通道只支持tansferFrom()方法
举个小例子来说明这个过程:
Java代码 复制代码
  1. File fromFile = new File("D:/from.txt");   
  2. File toFile = new File("D:/to.txt");   
  3. FileInputStream fis = new FileInputStream(fromFile);   
  4. FileOutputStream fos = new FileOutputStream(toFile);   
  5. FileChannel inChannel = fis.getChannel();   
  6. FileChannel outChannel = fos.getChannel();   
  7. int byteWritten = 0;   
  8. long byteCount = inChannel.size();   
  9. while(byteWritten < byteCount){   
  10.   byteWritten += inChannel.transferTo(bytesWritten,byteCount- bytesWritten,outChannel);   
  11. }   
  12. fis.close();   
  13. fos.close();  

也可以使用outChannel的transferFrom()方法复制数据,他们都可以一次传送所有的
数据,万一没有一次传送所有的数据,可以使用类似上面的while循环来处理。
三、随机读取文件:
FileChannel定义了两个方法用来进行随机读写操作:
int read(ByteBuffer buf,long position);
从文件的position位置开始读取字节到buf中
int write(ByteBuffer buf,long position);
把buf的数据,从文件的position开始写入到文件中
四、一个通道进行文件的读写
上面提到的从FileInputStream和FileOutputStream获得的通道只能进行读或者写操作,
如果我们想得到一个通道,同时可以进行读和写操作,那该怎么办?
从RandomAccessFile获得的通道就是这样一个通道。
Java代码 复制代码
  1. File file = new File("D:/test.txt");   
  2. RandomAccessFile raf = new RandomAccessFile(file,"rw");   
  3. FileChannel ioChannel = rad.getChannel();  

五、内存映像文件:
内存映像文件是这样的文件,它的内容被映像到计算机虚拟内存的一块区域,这样就可以
直接的使用和更新文件中的数据了,可以很大的提高效率。
FileChannel提供了map方法来把文件影射为内存映像文件:
MappedByteBuffer map(int mode,long position,long size);
可以把文件的从position开始的size大小的区域映射为内存映像文件,mode指出了
可访问该内存映像文件的方式:READ_ONLY,READ_WRITE,PRIVATE.
详细可以参考java API
MappedByteBuffer类还提供了三个方法:
fore();缓冲区是READ_WRITE模式下,此方法对缓冲区内容的修改强行写入文件
load()将缓冲区的内容载入内存,并返回该缓冲区的引用
isLoaded()如果缓冲区的内容在物理内存中,则返回真,否则返回假
Java代码 复制代码
  1. File file = new File("D:/test.txt");   
  2. RandomAccessFile raf = new RandomAccessFile(file,"rw");   
  3. FileChannel ioChannel = rad.getChannel();   
  4. MappedByteBuffer buf = ioChannel.map(READ_WRITE.0L,ioChannel.size()).load();  

后面总结有关从Socket获得到SocketChannel进行异步通讯的问题。
 
 
另外补充一下读写基本类型的例子:
写入基本类型:
Java代码 复制代码
  1. public static void testWritePrimitiveBuffer()throws Exception{   
  2.       int i = 10;   
  3.       long l = 10L;   
  4.       double d = 29.01;   
  5.       char ch = 'c';   
  6.       ByteBuffer buffer = ByteBuffer.allocate(100);   
  7.       buffer.putInt(i).putLong(l).putDouble(d).putChar(ch);   
  8.       FileOutputStream fos = new FileOutputStream(new File("D:/primitive.txt"));   
  9.       FileChannel outputChannel = fos.getChannel();   
  10.       buffer.flip();   
  11.       outputChannel.write(buffer);   
  12.    }  

读入基本类型:
将刚写入的数据读出:
Java代码 复制代码
  1. public static void testReadPrimitiveBuffer()throws Exception{   
  2.        int i;   
  3.        long l;   
  4.        double d;   
  5.        char ch;   
  6.        ByteBuffer buffer = ByteBuffer.allocate(100);   
  7.        FileInputStream fis = new FileInputStream(new File("D:/primitive.txt"));   
  8.        FileChannel inputChannel = fis.getChannel();   
  9.        inputChannel.read(buffer);   
  10.        buffer.flip();   
  11.        i = buffer.getInt();   
  12.        l = buffer.getLong();   
  13.        d = buffer.getDouble();   
  14.        ch = buffer.getChar();   
  15.        System.out.println("i=" + i + ",l=" + l + ",d=" + d + ",ch=" + ch);   
  16.     }  

Java nio(四)

关键字: java nio, socket channel

本来打算昨天总结这部分的,一方面昨天上了一天课,另一方面,这部分想理清全部的思路
并不那么容易.看《Java nio》那本书,差点没看崩溃,洋洋洒洒写了近三百大页介绍java nio,写的太散太细,反而觉得很乱,理不清思路了。那本书用起来作为参考手册还行,想很快上手这本书还真不适合。
今天打算总结和Socket异步通信相关的内容。
传统的Socket是阻塞,像ServerSocket在调用accept方法后便处于阻塞状态等待Client端的连接,所以一般会在Server端使用许多线程,对每一个Socket连接分配一个线程。充分利用并发特性来提高性能。
但这样会带来许多问题:
1、Server端创建了许多线程来处理Socket连接,而这些线程大部分的时间都在等待连接,
也就是说这些线程占了资源,真正做事情的时间却不多。也就是说资源利用率比较低,这就会
直接导致一个问题,可伸缩性比较差,当接受1000个连接还可以,但增加到10000个或更多时性能会很快下降。像Web服务器Jetty和Tomcat6.x都是用了异步通信模式,来接受客户端的连接,从而很大程度上提高了系统的伸缩性,提高了性能。
2、由于使用多线程,就会使问题变得复杂,事实如果你敢说精通并发编程,说明你太乐观了,并发编程很容易出错,并且你很难发现问题。并且需要互斥访问一些资源,这往往是个瓶颈,会降低并发性。
异步的Socket可以解决上面的问题,异步的Socket它是非阻塞的,它尝试去连接,但不管是否能够立即建立连接,它都会立即返回,返回之后它便可以做其他的事情了,但连接真正建立成功,就会有相应的事件来通知,这时候你去做连接成功之后的读写操作了.这个过程,整个线程都是处于忙碌状态,所以只需要单个或者很少几个线程,就可以达到阻塞方式的成百上千的线程的性能.
类似异步Socket功能应运而生,但Java在jdk1.4才引入这个功能,考虑以前Socket的已提供的功能,而且接口很难一致,Java并没有单独设计异步Socket,而是在java nio中引入了SocketChannel之类的通道来处理这个问题,我们会发现这些通道和对应的Socket提供的接口有很大的相似之处,但这些Channel不是关于Socket的抽象,它们并不处理TCP/UDP协议,而是委托给对Socket来处理。
这种新的Channel可以在非阻塞的模式下操作。通过注册Selector,使用一种类似观察者模式。其实是Selector不断的轮询连接的端口,我们可以通过Selector的select()方法,这个方法是阻塞的,他会更新就绪操作集的键的数目,并作为返回值返回。我们会通常在判断这个返回值如果不为零,则可能有我们感兴趣的事件发生,然后我们可以通过
Java代码 复制代码
  1. Iterator it = selector.selectedKeys().iterator();  

来得到键的迭代器,这样我们就可以通过遍历这个集合来判断有没有我们感兴趣的事情发生,如果有我们就做一些处理,没有我们可以把这个键remove掉:
Java代码 复制代码
  1. while(it.hasNext()){   
  2.     SelectionKey key = (SelectionKey)it.next();   
  3.     if(key.isAcceptable()){   
  4.        //do something   
  5.       }   
  6.      if(key.isReadable()){   
  7.         //read data            
  8.       }   
  9.     it.remove();//we remove the key beacause of we don't care about it   
  10. }  

一、下面我们介绍一下与Socket相关的各种Channel
与几种Socket对应的提供了一下几种Channel:
ServerSocketChannel,SocketChannel,DatagramChannel.
1、ServerSocketChannel:
Java代码 复制代码
  1. abstract  SocketChannel accept()    
  2.          // 接受到此通道套接字的连接。    
  3. static ServerSocketChannel open()    
  4.          //打开服务器套接字通道。    
  5. abstract  ServerSocket socket()    
  6.           //获取与此通道关联的服务器套接字。    
  7.  int validOps()    
  8.           //返回一个操作集,标识此通道所支持的操作。   

我们发现这个Channel根本不支持读写操作,叫Channel有点名不副实了,但Java中Channel本身的抽象也不一定支持读写操作的,需要实现WriteableChannel和ReadableChannnel接口才能支持。其实ServerSocketChannel本身也不是用于读写
操作的,它通常通过socket() 方法获取相关的ServerSocket,然后通过ServerSocket 方法bind来绑定端口。ServerSocketChannel提供了open()静态工厂方法来创建ServerSocketChannel对象。同时ServerSocketChannel从AbstractSelectableChannel
继承了:
Java代码 复制代码
  1. SelectableChannel configureBlocking(boolean block)    
  2.           //调整此通道的阻塞模式。    
  3. SelectionKey register(Selector sel, int ops)    
  4.           //向给定的选择器注册此通道,返回一个选择键。   

这两个方法是最常用的了。通过configureBlocking来设置通道的是否为阻塞模式,
通过register向给定的选择器注册此通道。
从上面的过程我们用代码总结一下一般的流程:
Java代码 复制代码
  1.           ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();   
  2.           ServerSocket serverSocket = serverSocketChannel.socket();   
  3.           Selector selector = Selector.open();   
  4.           serverSocket.bind(new InetSocketAddress(port));   
  5.           serverSocketChannel.configureBlocking(false);   
  6.           serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);   
  7.           while (true) {   
  8.           int n  = selector.select();   
  9.           if( n == 0)   
  10.          continue;   
  11.           Iterator it = selector.selectedKeys().iterator();   
  12.           while(it.hasNext()){   
  13.             SelectionKey key = (SelectionKey)it.next();   
  14.                 if(key.isAcceptable()){   
  15.                 ServerSocketChannel server =   
  16.                                 (ServerSocketChannel) key.channel();   
  17.                                 SocketChannel channel = server.accept();//获取SocketChannel来通信   
  18.                                 registerChannel (selector, channel,   
  19. SelectionKey.OP_READ);       doSomething(channel);   
  20.                 }   
  21.                 if(key.isReadable()){   
  22.                     readDataFormSocket(key);   
  23.                 }   
  24.                 it.remove();   
  25.             }   
  26.           }  

2、SocketChannel:
SocketChannel通常作为客户端,建立一个对Server端的连接,任何一个SocketChannel
都是和对应的Socket关联的,但一个Socket并不一定有SocketChannel可以获得。
同样的SocketChannel也适用静态工厂方法open()来实例化SocketChannel.
下面来看看最常用的操作:
引用

public abstract boolean connect(SocketAddress remote)
                         throws IOException连接此通道的套接字。
如果此通道处于非阻塞模式,则调用此方法会发起一个非阻塞连接操作。如果立即建立连接(使用本地连接时就是如此),则此方法返回 true。否则此方法返回 false,并且必须在以后通过调用 finishConnect 方法来完成该连接操作。

我们可以通过
Java代码 复制代码
  1. socketChannel.connect (new InetSocketAddress ("somehost", somePort));  

来建立连接,这个过程是异步的,他会立即返回,如果立即建立连接成功则返回true,否则
返回false.随后可以通过finishConnect来完成连接的建立。
另外可通过:
Java代码 复制代码
  1. SocketChannel socketChannel =   
  2. SocketChannel.open (new InetSocketAddress ("somehost", somePort));  
建立连接
等价于:
Java代码 复制代码
  1. SocketChannel socketChannel = SocketChannel.open();   
  2. socketChannel.connect (new InetSocketAddress ("somehost", somePort));  

下面我们演示一下整个的使用过程:
Java代码 复制代码
  1. InetSocketAddress addr = new InetSocketAddress (host, port);   
  2. SocketChannel sc = SocketChannel.open();   
  3. sc.configureBlocking (false);   
  4. sc.connect (addr);   
  5. while ( ! sc.finishConnect()) {   
  6. doSomethingElse();   
  7. }   
  8. doSomethingWithChannel (sc);   
  9. sc.close();  

3、DatagramChannel
这个Channel与DatagramSocket相对应,提供了基于UDP协议的数据包的套接字的通道。
UDP协议是无连接的,DatagramChannelt既可以作为Server端,也可以作为Client端,如果想新创建一个DatagramChannel作为Server端来监听,那么需要绑定到特定的端口或地址和端口的组合,一般过程如下:
Java代码 复制代码
  1. DatagramChannel channel = DatagramChannel.open();   
  2. DatagramSocket socket = channel.socket();   
  3. socket.bind (new InetSocketAddress (portNumber));  

但是一个没有绑定特定端口的DatagramChannel仍然是可以接收数据的,事实上会有一个
动态生成的端口分配给他。不管DatagramChannel是否绑定到了一个端口,任何一个包的发送都会包含它的地址,下面是DatagramChannel提供的发送和接收数据的方法:
Java代码 复制代码
  1. SocketAddress receive (ByteBuffer dst) throws  
  2. IOException;   
  3. int send (ByteBuffer src, SocketAddress target)  

DatagramChannel并不能保证数据能够发送到目的端,因为UDP协议本身就是不可靠的。
另外我们再看看DatagramChannel提供了以下下几个方法:
Java代码 复制代码
  1. public abstract DatagramChannel connect (SocketAddress remote)   
  2. throws IOException;   
  3. public abstract boolean isConnected();   
  4. public abstract DatagramChannel disconnect() throws IOException;  

从名字看起来很让人迷惑,因为DatagramChannel就是基于无连接的,为什么还会有
connect之类的方法呢?
其实这个Connect的语义和基于流的Socket是不一样的,这里的连接只是制定了远程的
地址,这样就可以忽略其他地址发来的数据包了。一但使用完connect方法,就不能像其
他的地址send数据了。但这里的connect和SockectChannel的connect不同,它是可以
随时disconnect,然后去connect其他的地址的。但使用了connect方法之后,就可以
像FileChannel那样read,write数据,而不用指名地址了。

 

 

 

来自:http://fuliang.javaeye.com/blog/