绑定本地地址:
this.serverSocketChannel。bind(new InetSocketAddress("localhost",8080), 100);其中的100用于指定等待连接的队列大小(backlog)。完了吗?还没有,重要的监听工作还没开始,监听端口是为了等待连接上来以便 accept产生一个AsynchronousSocketChannel来表示一个新建立的连接,因此需要发起一个accept调用,调用是异步的,操作系统将在连接建立后,将后的结果——AsynchronousSocketChannel返回给你:
public void pendingAccept(){
if (this.started this.serverSocketChannel.isOpen()) { this.acceptFuture = this.serverSocketChannel.accept(null,
new AcceptCompletionHandler());
} else {
throw new IllegalStateException("Controller has been closed");
}
注意,重复的accept调用将会抛出PendingAcceptException,后文提到的read和write也是如此。accept方法的个参数是你想传给CompletionHandler的attchment,第二个参数就是注册的用于回调的CompletionHandler,后返回结果Future.你可以对future做处理,这里采用更推荐的方式就是注册一个CompletionHandler.那么accept的 CompletionHandler中做些什么工作呢?显然一个赤裸裸的AsynchronousSocketChannel是不够的,我们需要将它封装成session,一个session表示一个连接(mina里就叫IoSession了),里面带了一个缓冲的消息队列以及一些其他资源等。在连接建立后,除非你的服务器只准备接受一个连接,不然你需要在后面继续调用pendingAccept来发起另一个accept请求:
private final class AcceptCompletionHandler implements
CompletionHandler {
@Override
public void cancelled(Object attachment){
logger.warn("Accept operation was canceled");
}
@Override
public void completed(AsynchronousSocketChannel socketChannel,
Object attachment){
try {
logger.debug("Accept connection from " + socketChannel.getRemoteAddress());
configureChannel(socketChannel);
AioSessionConfig sessionConfig = buildSessionConfig(socketChannel);
Session session = new AioTCPSession(sessionConfig,AioTCPController.this.configuration。 getSessionReadBufferSize(),AioTCPController.this.sessionTimeout);session.start();
registerSession(session);
} catch(Exception e){
e.printStackTrace();logger.error("Accept error", e);
notifyException(e);
} finally {
pendingAccept();
}
@Override
public void failed(Throwable exc, Object attachment) { logger.error("Accept error", exc);
try {
notifyException(exc);
} finally {
pendingAccept();
}
注意到了吧,我们在failed和completed方法中在后都调用了pendingAccept来继续发起accept调用,等待新的连接上来。有的同学可能要说了,这样搞是不是递归调用,会不会堆栈溢出?实际上不会,因为发起accept调用的线程与CompletionHandler回调的线程并非同一个,不是一个上下文中,两者之间没有耦合关系。要注意到,CompletionHandler的回调共用的是 AsynchronousChannelGroup绑定的线程池,因此千万别在回调方法中调用阻塞或者长时间的操作,例如sleep,回调方法能支持超时,防止线程池耗尽。
连接建立后,怎么读和写呢?回忆下在nonblocking nio框架中,连接建立后的件事是干什么?注册OP_READ事件等待socket可读。异步IO也同样如此,连接建立后马上发起一个异步read调用,等待socket可读,这个是Session.start方法中所做的事情:
public class AioTCPSession {
protected void start0(){
pendingRead();
}
protected final void pendingRead(){
if (!isClosed() this.asynchronousSocketChannel.isOpen()) { if (!this.readBuffer.hasRemaining()) { this.readBuffer = ByteBufferUtils。increaseBufferCapatity(this.readBuffer);
}
来源:考试大-Java认证
责编:zj 评论 纠错