结合具体的Java Socket编程讨论使用NIO提高服务端程序的性能的问题
Java NIO增加了新的SocketChannelServerSocketChannel等类来提供对构建高性能的服务端程序的支持 SocketChannelServerSocketChannel能够在非阻塞的模式下工作它们都是selectable的类在构建服务器或者中间件时推荐使用Java NIO
在传统的网络编程中我们通常使用一个专用线程(Thread)来处理一个Socket连接通过使用NIO一个或者很少几个Socket线程就可以处理成千上万个活动的Socket连接
通常情况下通过ServerSocketChannelopen()获得一个ServerSocketChannel的实例通过SocketChannelopen或者serverSocketChannelaccept()获得一个SocketChannel实例要使ServerSocketChannel或者SocketChannel在非阻塞的模式下操作可以调用
sernfigureBlocking (false);
或者
nfigureBlocking (false);
语句来达到目的通常情况下服务端可以使用非阻塞的ServerSocketChannel这样服务端的程序就可以更容易地同时处理多个socket线程
下面我们来看一个综合例子这个例子使用了ServerSocketChannelSocketChannel开发了一个非阻塞的能处理多线程的Echo服务端程序见示例
【程序源代码】
// ==================== Program Discription ===================== // 程序名称示例 : SocketChannelDemojava // 程序目的学习Java NIO#SocketChannel // ============================================================== import javanioByteBuffer; import javaniochannelsServerSocketChannel; import javaniochannelsSocketChannel; import javaniochannelsSelector; import javaniochannelsSelectionKey; import javaniochannelsSelectableChannel; import Socket; import ServerSocket; import InetSocketAddress; import javautilIterator; public class SocketChannelDemo { public static int PORT_NUMBER = ;//监听端口 ServerSocketChannel serverChannel; ServerSocket serverSocket ; Selector selector ; private ByteBuffer buffer = ByteBufferallocateDirect (); public static void main (String [] args) throws Exception { SocketChannelDemo server=new SocketChannelDemo(); serverinit(args); serverstartWork(); } public void init (String [] argv)throws Exception { int port = PORT_NUMBER; if (argvlength > ) { port = IntegerparseInt (argv []); } Systemoutprintln (Listening on port + port); // 分配一个ServerSocketChannel serverChannel = ServerSocketChannelopen(); // 从ServerSocketChannel里获得一个对应的Socket serverSocket = serverChannelsocket(); // 生成一个Selector selector = Selectoropen(); // 把Socket绑定到端口上 serverSocketbind (new InetSocketAddress (port)); //serverChannel为非bolck nfigureBlocking (false); // 通过Selector注册ServerSocetChannel serverChannelregister (selector SelectionKeyOP_ACCEPT); } public void startWork()throws Exception { while (true) { int n = selectorselect();//获得IO准备就绪的channel数量 if (n == ) { continue; // 没有channel准备就绪继续执行 } // 用一个iterator返回Selector的selectedkeys Iterator it = selectorselectedKeys(erator(); // 处理每一个SelectionKey while (ithasNext()) { SelectionKey key = (SelectionKey) itnext(); // 判断是否有新的连接到达 if (keyisAcceptable()) {//返回SelectionKey的ServerSocketChannel ServerSocketChannel server =(ServerSocketChannel) keychannel(); SocketChannel channel = serveraccept(); registerChannel (selector channel SelectionKeyOP_READ); doWork (channel); } // 判断是否有数据在此channel里需要读取 if (keyisReadable()) { processData (key); } //删除 selectedkeys itremove(); } } } protected void registerChannel (Selector selector SelectableChannel channel int ops) throws Exception { if (channel == null) { return; } nfigureBlocking (false); channelregister (selector ops); } //处理接收的数据 protected void processData (SelectionKey key) throws Exception { SocketChannel socketChannel = (SocketChannel) keychannel(); int count; bufferclear(); // 清空buffer // 读取所有的数据 while ((count = socketChannelread (buffer)) > ) { bufferflip(); // send the data don′t assume it goes all at once while (bufferhasRemaining()) { //如果收到回车键则在返回的字符前增加[echo]$字样 if(bufferget()==(char)) { bufferclear(); bufferput([echo]___FCKpd___quot;getBytes()); bufferflip(); } socketChannelwrite (buffer);//在Socket里写数据 } bufferclear(); // 清空buffer } if (count < ) { // count<说明已经读取完毕 socketChannelclose(); } } private void doWork (SocketChannel channel)throws Exception { bufferclear(); bufferput (HelloI am workingplease input some thingand i will echo to you![echo] ___FCKpd___quot;getBytes()); bufferflip(); channelwrite (buffer); } }
使用运行此程序然后在控制台输入命令telnet localhost