前言由于打算用java编写异步通信的服务端和客户端程序,所以笔者学习使用java.nio开发包。过程中遇到了一些问题,但是发现网上对其应用的描述不多。因此,笔者不遗余力地做一些简单的探讨,以供大家进一步探讨。相关类java.nio.*的简单介绍,据说它提供了一些更底层的功能,例如:类似于windows环境下的AsyncSocket类的异步操作功能,可以显着降低服务器的线程管理开销-端程序。因为大多数应用程序都是建立在TCP之上的,所以我这里只讲SocketChannel、ServerSocketChannel、Selector和ByteBuffer类。前三个最终都派生自通道类。通道类可以理解为具体I/O或文件对象之上的抽象操作对象。我们可以通过操作通道读写来读写相应的文件或I/O对象(包括socket)。读写的内容放在内存中ByteBuffer类提供的缓冲区中。总而言之,channel作为一个桥梁,连接着I/O对象和内存中的ByteBuffer,实现了更高效的I/O访问。一个基于TCP的服务器端程序必须有一个监听端和若干个通信端,由nio中对应的ServerSocketChannel和SocketChannel类实现。为了达到异步I/O操作的目的,需要Selector类,它可以检测I/O对象的状态。SocketChannel类是一个抽象类。通过调用它的静态函数open(),可以生成一个SocketChannel对象。该对象对应一个java.net.Socket,可以通过SocketChannel.socket()获取,其对应的Socket也可以通过调用getChannel()函数获取对应已建立的SocketChannel。SocketChannel和它的socket是一一对应的。SocketChannel的操作也和Socket很相似。ServerSocketChannel也是通过调用它的静态函数open()生成的,但是它不能直接调用bind()函数来绑定地址,需要它对应的ServerSocket来完成绑定工作。一般可以这样实现:ServerSocketChannelssc=newServerSocketChannel.open();ssc.socket().bind(InetSocketAddress(host,port));隔了半天,来看看最简单的C/S实现。服务器提供基本的回显功能。其中提供了更详细的注释。源码分析1.服务器端://////////////////////AsyncServer.java//byzztudou@163.com//////////////////////importjava.nio.channels.SocketChannel;importjava.nio.channels.ServerSocketChannel;importjava.nio.channels.SelectionKey;importjava.nio.channels.Selector;importjava.nio.ByteBuffer;importjava.nio.channels.SelectableChannel;importjava.nio.channels.spi.SelectorProvider;importjava.net.ServerSocket;importjava.net.Socket;importjava.net.InetSocketAddress;importjava.net.SocketAddress;importjava。util.Iterator;importjava.util.LinkedList;importjava.io.IOException;classAsyncServerimplementsRunnable{privateByteBuffer_buff=ByteBuffer.allocate(1024);privateByteBufferw_buff=ByteBuffer.allocate(1024);readprivatestaticintport=8848;publicAsyncSnew();}publicvoidrun(){try{//生成监听ServerSocketChannelssc=ServerSocketChannel.open();//设置监听为异步模式ssc.configureBlocking(false);//生成信号监听Selectors=Selector.open();//监听end绑定到端口ssc.socket().bind(newInetSocketAddress(port));//设置监听器选择的异步信号OP_ACCEPTssc.register(s,SelectionKey.OP_ACCEPT);System.out.println("echoserverhasbeensetup...");while(true){intn=s.select();if(n==0){//没有指定的I/O事件发生continue;}Iteratorit=s.selectedKeys().iterator();while(it.hasNext()){SelectionKeykey=(SelectionKey)it.next();if(key.isAcceptable()){//监听信号触发ServerSocketChannelserver=(ServerSocketChannel)key.channel();//接受新连接SocketChannelsc=server.accept();sc.configureBlocking(false);//设置socket的异步信号OP_READ:当socket可读时,//触发函数DealwithData();sc.register(s,SelectionKey.OP_READ);}if(key.isReadable()){//一个socket可读信号通过key指定socketchannelSocketChannelsc=(SocketChannel)key.channel();r_buff.clear();//读取数据到r_buffwhile((count=sc.read(r_buff))>0);//保证r_buff可读r_buff.flip();w_buff.clear();//将r_buff内容复制到w_buffw_buff中。put(r_buff);w_buff.flip();//返回数据到客户端EchoToClient(sc);w_buff.clear();r_buff.clear();}publicvoidEchoToClient(SocketChannelsc)throwsIOException{while(w_buff.hasRemaining())sc.write(w_buff);}publicstaticvoidmain(Stringargs[]){if(args.length>0){port=Integer.parseInt(args[0]);}newAsyncServer();}}在当前目录运行:javacAsynServer.java后,如果没有编译错误,那么就可以运行:javaAsynServer或javaAsynServer×××(端口号)当上面服务程序运行时,可以指定其监听端口,否则程序会默认使用8848端口2.客户端简单示例:///////////////////////AsyncClient.java//byzztudou@163.com//////////////////////importjava.nio.channels.SocketChannel;importjava.net.InetSocketAddress;importjava.nio.ByteBuffer;importjava.nio.channels.Selector;importjava。nio.频道privatestaticStringhost;privatestaticintport=8848;publicAsyncClient(){try{InetSocketAddressaddr=newInetSocketAddress(host,port);//生成一个socketchannelsc=SocketChannel.open();//连接到服务器sc.connect(addr);while(!sc.finishConnect());System.out.println("connectionhasbeenestablished!...");while(true){//EchomessageStringecho;try{System.err.println("Entermsgyou'dliketosend:");BufferedReaderbr=newBufferedReader(新的输入字符串eamReader(System.in));//输入回显信息echo=br.readLine();//将回显信息放入w_buffw_buff.clear();w_buff.put(echo.getBytes());w_buff.flip();}catch(IOExceptionioe){System.err.println("sth.iswrongwithbr.readline()");}//发送消息while(w_buff.hasRemaining())sc.write(w_buff);w_buff.clear();//进入接收状态Rec();//间隔1秒Thread.currentThread().sleep(1000);}}catch(IOExceptionioe){ioe.printStackTrace();}catch(InterruptedExceptionie){ie.printStackTrace();}}///////////////读取服务器发回的数据并显示publicvoidRec()throwsIOException{intcount;r_buff.clear();count=sc.read(r_buff);r_buff.flip();byte[]temp=newbyte[r_buff.limit()];r_buff.get(temp);System.out.println("replyis"+count+"long,andcontentis:"+newString(temp));}publicstaticvoidmain(Stringargs[]){if(args.length<1){//输入需要有主机名或IP地址try{System.err.println("Enterhostname:");BufferedReaderbr=newBufferedReader(newInputStreamReader(System.in));host=br.readLine();}catch(IOExceptionioe){System.err.println("sth.iswrongwithbr.readline()");}}elseif(args.length==1){host=args[0];}elseif(args.length>1){host=args[0];port=Integer.parseInt(args[1]);}newAsyncClient();}}在当前目录运行:javacAsynClient.java,如果没有编译错误,确认AsyncServer已经运行,那么就可以运行:javaAsynClienthostname或者javaAsynClienthostname×××(端口号),按提示总结,总的来说,使用nio进行网络编程还是很有创新的,服务器端的软件可以在一个线程中维护与多个客户端的通信连接,作者写在这篇文章我尝试用一??个典型的逆向反射例子来说明如何使用nio构建最基本的C/S应用,希望大家可以尝试使用,另外笔者在nio的应用中也发现了一些问题实践,比如如何应用SocketChannel的继承类,以及如何在socketchannel之上应用SSL(SecureSocketLayer)等等,所以我希望这篇文章只是一个介绍,以引发对nio的进一步讨论。原文链接:http://lrtlcg.iteye.com/blog/844357【编者推荐】JavaNIO唤醒分析JavaNIO类库关系图分析TomcatNIO配置JavaNIOAPI详解JavaNIO基本使用实例
