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

原创 2014年03月01日 22:46:50

关于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<SelectionKey> 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 Socket收发异步长连接

最近做SSO的项目,其中用到了socket长连接.一直都是看代码的,但是要求socket做成SSL的,不得不动手写写代码了.下面我给出一个简单的socket长连接. Java代码 ...

Java NIO(异步IO)Socket通信例子

简要说明下:使用java nio开发网络通讯 是比较快速和方面的。因为他可以不用阻塞的方式侦听客户端的连接 ,在java nio中可以使用基于事件的机制进行非阻塞通讯,当有新的事 件进行注册时 我们...

java异步socket调用

jdk供的无阻塞I/O(NIO)有效解决了多线程服务器存在的线程开销问题,但在使用上略显得复杂一些。在NIO中使用多线程,主要目的已不是为了应对每个客户端请求而分配独立的服务线程,而是通过多线程充分使...
  • nicklsq
  • nicklsq
  • 2012年03月14日 14:15
  • 8609

java server 多client异步socket通信demo

本demo打包下载,请点击这里 本demo主要用java实现了服务器监听多客户端登录,并实现了客户端与服务器的异步socket通信,通信过程采用了消息队列缓冲机制(生产者消费者模式)。 登录过程是...

Aio--Java异步IO的 Socket Demo

package com.vdebug.aio.socket; import java.io.IOException; import java.net.InetSocketAddress; impor...
  • xxb2008
  • xxb2008
  • 2015年01月05日 17:09
  • 7464

Java nio 实现socket异步通信 (对Java nio 实习笔记五中内容做一纠正)

原始错误版本请看:http://blog.csdn.net/tsyj810883979/article/details/6877216 在原有基础上考虑了编码与解码的问题,还有消息发送的两个重要方法...

Java Socket编程(四) 异步服务器

基于Java Socket给出了异步服务器的实现

面试题一:实现两个线程交替打印数字

public class Solution1 { private static Object lock = new Object(); private static int i = 1; p...

加入ehcache后,系统出现内存泄漏,解决办法

最近在系统中,加入缓存ehcache,但发现,每隔一天,服务器就会报出内存溢出。 问题严重,后来在网上查资料发现,一篇解释的网文: spring中的提供了一个名为org.springframewo...

Thinking in Java--使用NIO实现非阻塞Socket通信

Java1.4提供了一种新的IO读取方式,称为NIO。NIO中使用了通道和缓冲器的概念,并且以块的形式操作数据,这样更接近操作系统IO操作的形式,提高了JavaIO的效率。NIO的核心类有两个Chan...
  • acm_lkl
  • acm_lkl
  • 2015年08月24日 21:45
  • 1043
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:使用Java NIO实现异步的socket通信
举报原因:
原因补充:

(最多只允许输入30个字)