java nio学习:简单的echo服务

代码实现了一个简单单线程的echo服务,首先创建并绑定监听通道,接着注册到选择器中,选择器等待事件的发生,当有连接到来时创建新的连接并注册读事件,当连接有数据可读时读取并返回数据,当对端关闭连接时则注销事件并关闭连接

ServerSocketChannel acceptor = ServerSocketChannel.open();//创建监听通道
acceptor.configureBlocking(false);
acceptor.socket().bind(new InetSocketAddress(80));

Selector selector = Selector.open();

acceptor.register(selector, SelectionKey.OP_ACCEPT);//注册监听事件

while(true)
{
	int nready = selector.select();//等待事件的发生
	if(nready <= 0)
		continue;
	
	Set<SelectionKey> keys = selector.selectedKeys();//获取选择键
	
	for (SelectionKey key : keys)
	{				
		if(key.isAcceptable())//有连接到来
		{
			ServerSocketChannel channel = (ServerSocketChannel) key.channel();
			SocketChannel client = channel.accept();
			client.configureBlocking(false);
			client.register(selector, SelectionKey.OP_READ);
		}
		else if(key.isReadable())//连接可读
		{
			SocketChannel client = (SocketChannel) key.channel();
			ByteBuffer buf = ByteBuffer.allocate(1024);
			int n = 0;
			
			while( (n = client.read(buf)) > 0)
			{
				buf.flip();
				
				client.write(buf);//返回数据<pre name="code" class="java"><pre name="code" class="java">                                buf.clear();
}if(n < 0){ key.cancel();client.close();}}}keys.clear();}
 
 

 下面实现一个多线程的echo服务,主线程负责监听并接受连接,接着把新创建的连接放入子线程的连接队列中,子线程处理连接数据的收发 

实现子线程的Runnable接口类

class Task implements Runnable
{
	BlockingQueue<SocketChannel> queue = new LinkedBlockingQueue<SocketChannel>();
	Selector selector;
	
	public Task()
	{
		try
		{
			selector = Selector.open();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public void run()
	{
		try
		{
			while(true)
			{
				selector.select();
				
				Set<SelectionKey> keys = selector.selectedKeys();
				for (SelectionKey key : keys)//处理数据的收发
				{
					if(key.isReadable())
					{
						SocketChannel channel = (SocketChannel) key.channel();
						ByteBuffer buf = ByteBuffer.allocate(1024);
						int n = 0;
						
						while((n = channel.read(buf)) > 0)
						{
							buf.flip();
							
							channel.write(buf);
							
							buf.clear();
						}
										
						key.cancel();
						channel.close();
					}
				}
				
				keys.clear();
				
				while(!queue.isEmpty())//连接队列不为空
				{
					SocketChannel channel = queue.poll();
					channel.configureBlocking(false);
					channel.register(selector, SelectionKey.OP_READ);//注册读事件
				}
			}
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
}

public class EchoServer 
{
	private int port = 80;
	private ServerSocketChannel listener;
	private Selector selector;
	private ArrayList<Task> threads = new ArrayList<Task>();
	private int index = -1;
	private int nthreads = 4;
	
	public EchoServer()
	{
		try
		{
			selector = Selector.open();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public EchoServer(int port)
	{
		this();
		
		this.port = port;
	}
	
	public void setThreadNumber(int n)
	{
		if(n > 0)
			nthreads = n;
	}
	
	public void start()
	{
		try
		{
			listener = ServerSocketChannel.open();
			listener.configureBlocking(false);
			listener.socket().bind(new InetSocketAddress(port));
			
			listener.register(selector, SelectionKey.OP_ACCEPT);
			
			for(int i = 0; i < nthreads; i++)
			{
				Task task = new Task();
				
				new Thread(task).start();//创建子线程
				
				threads.add(task);
			}
			
			while(true)
			{
				int nready = selector.select();
				if(nready <= 0)				
					continue;
			
				onAccept();//有新连接
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	private void onAccept()
	{
		try
		{
			SocketChannel client = listener.accept();
			if(client == null)
				return;

			index = (index + 1) % nthreads;//轮询子线程
			
			threads.get(index).queue.offer(client);//子线程中加入新连接
			threads.get(index).selector.wakeup();//唤醒子线程
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args)
	{
		EchoServer echoServer = new EchoServer(80);
		echoServer.setThreadNumber(4);
		echoServer.start();//启动echo服务
	}
}
在程序的测试过程中遇到了选择器select()调用bug,选择器上没有事件发生,select()不阻塞,一直返回0,浪费了CPU,可以回收旧的选择器,将注册的事件转移到新创建的选择器上

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值