import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class NioServer {
/*在构造时指定绑定的本端地址,*/
private InetSocketAddress localAddress = null;
private ServerSocketChannel serverChannel = null;
private Selector connectionSelector = null; /*处理serverChannel的连接事件*/
private Selector ioSelector = null; /*专门处理IO*/
private Thread task = null;
private Thread service = null;
private ThreadPoolExecutor connectionThreadpool = null;
private ThreadPoolExecutor serviceThreadpool = null;
private ConnectionPool connectionPool = new ConnectionPool();
private CmdSetHandler cmdsetHandler = new CmdSetHandler();
public NioServer(String localAddress,int localPort) {
try {
this.localAddress = new InetSocketAddress(InetAddress.getByName(localAddress), localPort);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void startAcceptorTask() {
ServerTask servertask = new ServerTask();
task = new Thread(servertask);
task.setName("ServerTask");
task.start();
}
private void startServiceTask() {
ServiceTask servicetask = new ServiceTask();
service = new Thread(servicetask);
service.setName("ServiceTask");
service.start();
}
public void launch() {
startAcceptorTask();
startServiceTask();
System.out.println("Server starting...");
}
private class ConnectionPool {
private ConcurrentHashMap<String,SocketChannel> connectionPool = new ConcurrentHashMap<String,SocketChannel>();
public void putConnection(String key,SocketChannel channel) {
connectionPool.put(key, channel);
}
public void removeConnection(String key) {
connectionPool.remove(key);
}
}
private class CmdSetHandler {
private LinkedBlockingQueue<CmdSetBase> cmdset = new LinkedBlockingQueue<CmdSetBase>();
public void addCmd(CmdSetBase cmd) {
try {
cmdset.put(cmd);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void executeCmdSet() {
Iterator<CmdSetBase> ite = cmdset.iterator();
while(ite.hasNext()) {
ite.next().execute();
ite.remove();
}
}
}
private abstract class CmdSetBase {
public abstract void execute();
}
private class CmdSetRegister extends CmdSetBase {
private SocketChannel channel = null;
private Selector selector = null;
private int interestOps = 0;
private Object attachment = null;
public CmdSetRegister(SocketChannel channel, Selector selector,int interestOps,Object attachment) {
super();
this.channel = channel;
this.selector = selector;
this.interestOps = interestOps;
this.attachment = attachment;
}
@Override
public void execute() {
// TODO Auto-generated method stub
try {
channel.register(selector, interestOps, attachment);
} catch (ClosedChannelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private class ServerConnectionThreadFactory implements ThreadFactory {
private int cnt = 0;
@Override
public Thread newThread(Runnable r) {
// TODO Auto-generated method stub
Thread acceptor = new Thread(r);
acceptor.setName("ServerConnectionThread["+(++cnt)+"]");
return acceptor;
}
}
private class ServerServiceThreadFactory implements ThreadFactory {
private int cnt = 0;
@Override
public Thread newThread(Runnable r) {
// TODO Auto-generated method stub
Thread serviceHandler = new Thread(r);
serviceHandler.setName("ServerServiceThread["+(++cnt)+"]");
return serviceHandler;
}
}
private class RejectPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// TODO Auto-generated method stub
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
executor.toString());
}
}
/*每个连接配置一个ServiceHandler,考虑到不同的连接可能会有数据独立性的需求,因此不用一个ServiceHandler对象分配多个线程,而是
* 每个连接的收发都新建一个ServiceHandler对象,然后分配给线程执行。*/
private class ServiceHandler implements Runnable {
private SelectionKey key = null;
private Set<SelectionKey> keySet = null;
private ByteBuffer sendbuffer = ByteBuffer.allocate(128);
private ByteBuffer recvbuffer = ByteBuffer.allocate(128);
public ServiceHandler() {
super();
}
public ServiceHandler setKey(SelectionKey key,Set<SelectionKey> keySet) {
this.key = key;
this.keySet = keySet;
return this;
}
private void readData() {
try {
recvbuffer.clear();
SocketChannel channel = (SocketChannel) key.channel();
int recvlen = channel.read(recvbuffer);
if( recvlen > 0 ) {
recvbuffer.flip();
/*打印一下收到的数据*/
System.out.println("Server:recv msg="+new String(recvbuffer.array(),0,recvlen));
/*要实现一个echo server,则将收到数据回填到sendbuffer,然后加入WRITE事件的关注*/
System.out.println("Server:send msg back to client.");
sendbuffer.clear();
sendbuffer.put(recvbuffer);
sendbuffer.flip();
key.interestOps(SelectionKey.OP_WRITE | SelectionKey.OP_READ);
} else if( recvlen < 0 ) {
channel.close();
key.cancel();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
try {
key.channel().close();
key.cancel();
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
} finally {
keySet.remove(key);
}
}
private void writeData() {
try {
if( sendbuffer.remaining() > 0 ) {
SocketChannel channel = (SocketChannel) key.channel();
channel.write(sendbuffer);
/*发送完毕后不再关注WRITE事件*/
key.interestOps(SelectionKey.OP_READ);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
keySet.remove(key);
}
}
@Override
public void run() {
// TODO Auto-generated method stub
if( key.isWritable() ) {
System.out.println("ServiceHandler:writable");
writeData();
} else if( key.isReadable() ) {
System.out.println("ServiceHandler:readable");
readData();
}
}
}
private class ConnectionHandler implements Runnable {
private SelectionKey key = null;
private Set<SelectionKey> keyset = null;
private SocketChannel clientChannel = null;
public ConnectionHandler(SelectionKey key,Set<SelectionKey> keyset) {
super();
this.key = key;
this.keyset = keyset;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
clientChannel = ((ServerSocketChannel)key.channel()).accept();
if( null != clientChannel ) {
clientChannel.configureBlocking(false);
InetSocketAddress remoteAddress = (InetSocketAddress)clientChannel.getRemoteAddress();
String remoteIP = remoteAddress.getHostString();
int remotePort = remoteAddress.getPort();
System.out.println("Server:Accepted connection from IP="+remoteIP+":port="+remotePort);
/*注册到service selector*/
cmdsetHandler.addCmd(new CmdSetRegister(clientChannel,ioSelector,SelectionKey.OP_READ,new ServiceHandler()));
ioSelector.wakeup(); /*让阻塞的selector立即返回,进而返回到循环的其实,然后执行 cmdsetHandler.executeCmdSet() */
/*加入到连接池*/
String key = remoteIP+":"+remotePort;
connectionPool.putConnection(key, clientChannel);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
keyset.remove(key); /*处理完后再删除,这样在处理过程中,selector会屏蔽掉该key的事件,不会再分配新的线程执行accept(重复的accept)*/
}
}
}
private class ServerTask implements Runnable {
private void initServerChannel() {
try {
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.bind(localAddress); /*default backlog is 5*/
System.out.println("Server:bind IP="+localAddress.getHostName()+":port="+localAddress.getPort());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void initSelector() {
try {
connectionSelector = Selector.open();
ioSelector = Selector.open();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void startAccept() {
try {
serverChannel.register(connectionSelector, SelectionKey.OP_ACCEPT);
} catch (ClosedChannelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void initThreadPool() {
connectionThreadpool = new ThreadPoolExecutor(3,
10,
20,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(100), /*使用有界队列,确保server不会被大量请求耗尽资源*/
new ServerConnectionThreadFactory(),
new RejectPolicy()) ;
serviceThreadpool = new ThreadPoolExecutor(3,
10,
20,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(100), /*使用有界队列,确保server不会被大量请求耗尽资源*/
new ServerServiceThreadFactory(),
new RejectPolicy()) ;
}
private void init() {
/*init thread-pool*/
initThreadPool();
/*init channel*/
initServerChannel();
/*init selector*/
initSelector();
/*start accepting connections*/
startAccept();
}
private void handleKey(SelectionKey key,Set<SelectionKey> keyset) {
if( true == key.isAcceptable() ) {
System.out.println("Server:Acceptable");
/*server启动监听后,如果没有连接到来,不会触发该事件。仅当有请求到来,server-socketchannel进入可接受请求的状态,或者有错误
* 发生的时候,才会触发此事件。*/
connectionThreadpool.execute(new ConnectionHandler(key,keyset));
} else {
System.out.println("Server:Not handled process.");
}
}
@Override
public void run() {
// TODO Auto-generated method stub
/*initialization process*/
init();
while(!Thread.currentThread().isInterrupted()) {
try {
int num = connectionSelector.select();
if( num > 0 ) {
Set<SelectionKey> keyset = connectionSelector.selectedKeys();
Iterator<SelectionKey> ite = keyset.iterator();
while(ite.hasNext()) {
handleKey(ite.next(),keyset);
//ite.remove();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private class ServiceTask implements Runnable {
private void handleKey(SelectionKey key,Set<SelectionKey> keySet) {
/*service处理只有读/写,交给ServiceHandler去处理,这个task的循环中只做分发。*/
serviceThreadpool.execute(((ServiceHandler)key.attachment()).setKey(key,keySet));
}
@Override
public void run() {
// TODO Auto-generated method stub
while(!Thread.currentThread().isInterrupted() ) {
if( null != ioSelector ) {
try {
cmdsetHandler.executeCmdSet();
int num = ioSelector.select();
if( num > 0 ) {
System.out.println("ioSelector set number = "+num);
Set<SelectionKey> keySet = ioSelector.selectedKeys();
Iterator<SelectionKey> ite = keySet.iterator();
while(ite.hasNext()) {
handleKey(ite.next(),keySet);
//ite.remove();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
/*NIO几个大坑:
* 1、每次获取一个新的SocketChannel,都需要设置其阻塞模式
* 无论是新open的还是通过accept方法返回的,都需要设置,否则默认是blocking模式。
* 2、Selector.selectedKeys返回的key-set,处理完后需要clear
* 如果key不从key-set中删除,select会暂时屏蔽该key对应的channel的关注事件。
* 3、Selector有两种select方式,分别是阻塞式与非阻塞式
* 通常用select或select(time),这是阻塞模式,而selectNow会立即返回,不阻塞;
* selectNow的优先级比 wakeUp要高,如果调用wakeup的同时调用了selectNow,则立即执行后者,前者失效。
* 4、select方法与register方法均是阻塞的
* 如果selector被阻塞在select方法上,此时另外一个线程尝试将新的channel注册到此selector,则会被阻塞,直到select返回,
* 这是一个巨大的坑,注册的线程会被阻塞在register方法上,而selector不会返回任何东西。
* 5、OP_READ 事件
* 并不是数据到来时才触发这个事件,除了数据到来,还有:
* corresponding channel is ready for reading
has reached end-of-stream
has been remotely shut down for further reading
has an error pending
因此一旦抛异常,将channel.close掉,并将key.cancel掉。如果read返回-1,表示遇到EOS,则closechannel,或取消注册OP_READ
EOS表示对端关闭了socket或者关闭了输出。
6、问题
1、handle select如果采用分发线程的方式,则selectedKeys会重复的进入,导致产生多个功能一样的线程,如果规避这个问题,难道只能用循环处理,不能用异步的方式解决?
2、对月OP_WRITE以及OP_READ两个事件,需要更详细的说明
* */
【代码积累】NIO server
最新推荐文章于 2023-04-26 17:08:59 发布