|
java NIO增加了新的SocketChannel、ServerSocketChannel等类来提供对构建高性能的服务端程序的支持。 SocketChannel、ServerSocketChannel能够在非阻塞的模式下工作,它们都是selectable的类。在构建服务器或者中 间件时,推荐使用Java NIO。
在传统的网络编程中,我们通常使用一个专用线程(Thread)来处理一个Socket连接,通过使用NIO,一个或者很少几个Socket线程就可以处理成千上万个活动的Socket连接。
通 常情况下,通过ServerSocketChannel.open()获得一个ServerSocketChannel的实例,通过 SocketChannel.open或者serverSocketChannel.accept()获得一个SocketChannel实例。要使 ServerSocketChannel或者SocketChannel在非阻塞的模式下操作,可以调用
serverSocketChannel.configureBlocking (false);
或者
socketChannel.configureBlocking (false);
语句来达到目的。通常情况下,服务端可以使用非阻塞的ServerSocketChannel,这样,服务端的程序就可以更容易地同时处理多个socket线程。
下面我们来看一个综合例子,这个例子使用了ServerSocketChannel、SocketChannel开发了一个非阻塞的、能处理多线程的Echo服务端程序,见示例12-14。
【程序源代码】
[table=771px][tr][td][pre]1 // ==================== Program Discription =====================2 // 程序名称:示例12-14 : SocketChannelDemo.java3 // 程序目的:学习Java NIO#SocketChannel4 // ==============================================================5 6 7 import java.nio.ByteBuffer;8 import java.nio.channels.ServerSocketChannel;9 import java.nio.channels.SocketChannel;10 import java.nio.channels.Selector;11 import java.nio.channels.SelectionKey;12 import java.nio.channels.SelectableChannel;13 14 import java.net.Socket;15 import java.net.ServerSocket;16 import java.net.InetSocketAddress;17 import java.util.Iterator;1819 public class SocketChannelDemo 20 21 {22 public static int PORT_NUMBER = 23;//监听端口23 ServerSocketChannel serverChannel;24 ServerSocket serverSocket ;25 Selector selector ;26 private ByteBuffer buffer = ByteBuffer.allocateDirect (1024);27 28 public static void main (String [] args)29 throws Exception30 {31 SocketChannelDemo server=new SocketChannelDemo();32 server.init(args);33 server.startWork();34 }35 36 37 public void init (String [] argv)throws Exception38 {39 int port = PORT_NUMBER;40 41 if (argv.length > 0) { 42 port = Integer.parseInt (argv [0]);43 }44 45 System.out.println ("Listening on port " + port);46 47 // 分配一个ServerSocketChannel48 serverChannel = ServerSocketChannel.open();49 // 从ServerSocketChannel里获得一个对应的Socket50 serverSocket = serverChannel.socket();51 // 生成一个Selector52 selector = Selector.open();53 54 // 把Socket绑定到端口上55 serverSocket.bind (new InetSocketAddress (port));56 //serverChannel为非bolck57 serverChannel.configureBlocking (false);58 59 // 通过Selector注册ServerSocetChannel60 serverChannel.register (selector, SelectionKey.OP_ACCEPT); 61 62 }63 64 public void startWork()throws Exception 65 66 {67 while (true) {68 69 int n = selector.select();//获得IO准备就绪的channel数量70 71 if (n == 0) {72 continue; // 没有channel准备就绪,继续执行73 }74 75 // 用一个iterator返回Selector的selectedkeys76 Iterator it = selector.selectedKeys().iterator();77 78 // 处理每一个SelectionKey79 while (it.hasNext()) {80 SelectionKey key = (SelectionKey) it.next();8182 // 判断是否有新的连接到达83 if (key.isAcceptable()) {84 //返回SelectionKey的ServerSocketChannel85 ServerSocketChannel server =(ServerSocketChannel) key.channel();86 SocketChannel channel = server.accept();87 88 registerChannel (selector, channel,89 SelectionKey.OP_READ);90 91 doWork (channel);92 }93 94 // 判断是否有数据在此channel里需要读取95 if (key.isReadable()) {96 97 processData (key);98 99 }100 101 //删除 selectedkeys102 it.remove();103 }104 }105 }106 protected void registerChannel (Selector selector,107 SelectableChannel channel, int ops)108 throws Exception109 { 110 if (channel == null) {111 return; 112 }113 114 115 channel.configureBlocking (false);116 117 channel.register (selector, ops);118 }119 120 //处理接收的数据121 protected void processData (SelectionKey key)122 throws Exception123 {124 125 126 SocketChannel socketChannel = (SocketChannel) key.channel();127 int count;128 129 buffer.clear(); // 清空buffer130 131 // 读取所有的数据132 while ((count = socketChannel.read (buffer)) > 0) {133 buffer.flip(); 134135 // send the data, don′t assume it goes all at once136 while (buffer.hasRemaining())137 {138 //如果收到回车键,则在返回的字符前增加[echo]$字样139 if(buffer.get()==(char)13)140 {141 buffer.clear();142 buffer.put("[echo]___FCKpd___0quot;.getBytes());143 buffer.flip();144 145 }146 socketChannel.write (buffer);//在Socket里写数据147 }148 149 buffer.clear(); // 清空buffer150 }151 152 if (count < 0) {153 // count<0,说明已经读取完毕154 socketChannel.close(); 155 }156 }157 158 159 private void doWork (SocketChannel channel)throws Exception160 {161 buffer.clear();162 buffer.put ("Hello,I am working,please input some thing,and i will echo to you![echo] ___FCKpd___0quot;.getBytes());163 buffer.flip();164 channel.write (buffer);165 }166 167 }[/pre][/td][/tr][/table]
使用:运行此程序,然后在控制台输入命令telnet localhost 23。
【程序输出结果】如图所示:
|
|