使用Java NIO实现异步的socket通信

关于NIO的介绍,可以参考这一组文章,http://tutorials.jenkov.com/java-nio/index.html,这组教程写得比较好。

关于用旧IO实现的Socket通信,可以参考这一组文章:http://blog.csdn.net/kongxx/article/category/1077912


最近在学习NIO与Socket的时候,发现网上鲜见真正实现异步的socket的例子,大都仅仅使用Selector来管理服务端的ServerSocketChannel,事实上没有做到真正的异步,还是按部就班地接受连接,收发信息,然后释放连接。

更常规的方法是服务端使用两个线程,一个线程采用阻塞的方法来accept客户端,另一个线程用selector来异步地处理所有连接。

     

这里使用Selector来实现异步IO的一个简单实例。服务端在主线程中通过无限循环阻塞线程来监听将传入的连接、再启动另一个线程通过Selector来异步地处理已连接的通道。客户端启动100个线程来模拟100个用户同时连入服务端,从输出的结果可以看出客户端与服务端的通信是异步地完成的。

先贴出一个工具类,里面有收发消息的两个方法。



import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

public class SendAndReceiveUtil {
	public static String receiveData(SocketChannel channel) {
		// TODO Auto-generated method stub
		// TODO Auto-generated method stub
		ByteBuffer bb = ByteBuffer.allocate(1024);
		StringBuilder msg = new StringBuilder();
		Charset charset = Charset.forName("UTF-8");  
		CharsetDecoder decoder = charset.newDecoder();
		try {
			while( (channel.read(bb) ) > 0 ){
				bb.flip();		
				msg.append(decoder.decode(bb).toString());
				//System.out.println(msg.toString());
	
				bb.clear();
			}
			return msg.toString();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	public static  void sendData(SocketChannel socketChannel, String msg) {
		// TODO Auto-generated method stub
		try {
			socketChannel.write(ByteBuffer.wrap(msg.getBytes()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
然后贴出服务端的代码。



import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public class ChatServer {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		// TODO Auto-generated method stub
		final Selector selector = Selector.open();;
		
		ServerSocketChannel ssc = ServerSocketChannel.open();
		
		try{
			// Bind the server socket to the local host and port 
			ssc.socket().bind(new InetSocketAddress("localhost", 8080));
			
			//start a thread to handle the wirte and read
			startWRThread(selector);
			//block the main thread to accept client
			while(true){  // will block the thread
				
				SocketChannel sc = ssc.accept();
				//Get the server socket and set to non blocking mode  
				sc.configureBlocking(false);
				sc.register(selector, SelectionKey.OP_READ);
			}
		}finally{
			selector.close();
			ssc.close();
		}
	}

	private static void startWRThread(final Selector selector) {
		// TODO Auto-generated method stub
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub			
				try {
					while(true){
						while(selector.selectNow() > 0){
							
							Iterator it = selector.selectedKeys().iterator();
							//// Walk through the ready keys collection and process date requests.
							while(it.hasNext()){
								SelectionKey readyKey = it.next();
								if(readyKey.isReadable()){
									SocketChannel sc = (SocketChannel) readyKey.channel();
									 String msg = SendAndReceiveUtil.receiveData(sc);  
							         if(msg != null && !msg.equals("")) {
							        	 if(msg.equals("bye")){
							        		 System.out.println("Get a msg : " + msg);
							        		 sc.close();
							        	 }else{
							        		 System.out.println("Get a msg : " + msg);
							        		 SendAndReceiveUtil.sendData(sc,"Server have got you msg:"+ msg);
							        		 sc.shutdownOutput();
							        	 }	        	
							         }
							         
									 it.remove(); 
								}

								//execute((ServerSocketChannel) readyKey.channel());
							}
						}
					}
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}).start();
	}

}

最后贴出客户端代码,客户端启动100个线程模拟100个client来连接服务端,并发送线程id到服务端,随后发送”bye“来结束连接。



import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;




public class ChatClient {
	

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for (int i = 1; i < 100; i++) {  
            final int idx = i;  
            new Thread(new MyRunnable(idx)).start();  
        }  
	}
	private static class MyRunnable implements Runnable {

		private final int idx;  
  	  
        private MyRunnable(int idx) {  
            this.idx = idx;  
        }  
		@Override
		public void run() {
			// TODO Auto-generated method stub
			SocketChannel socketChannel = null;  
            try {  
                socketChannel = SocketChannel.open();  
                SocketAddress socketAddress = new InetSocketAddress("localhost", 8080);  
                
                socketChannel.connect(socketAddress);  
                
                SendAndReceiveUtil.sendData(socketChannel, "My id is " + idx);  
                  
                String msg = SendAndReceiveUtil.receiveData(socketChannel);  
                if(msg != null) System.out.println("The server reply:"+msg);
                
                SendAndReceiveUtil.sendData(socketChannel,"bye");
          
            } catch (Exception ex) {  
                ex.printStackTrace();
            } finally {  
                try {          
                    socketChannel.close();  
                } catch(Exception ex) {
               	 	ex.printStackTrace();
                }  
            }  
		}
		
		
	}
}



你可能感兴趣的:(JAVA,NIO相关)