先看下Mina的结构图,如果之前做过java的web开发,熟悉servlet规范,看到这个结构图,会觉得非常亲切。这个结构描述了基本的网络开发结构。
- IoService 服务端和客户端I/O 操作的抽象,服务端为IoAcceptor,客户端为IoConnector
- IoSession 封装了服务端与客服端连接的会话信息
- IoFilterChain IoFilter处理链
- IoFilter 对服务端和客户端交互的数据做处理
- IoHandler 业务处理
好了,说了那么多还是先分析代码吧。Mina的客户端和服务端开发会略有不同,因为java网络编程的本身就是如此。我们先从分析服务器端开发入手。这里引用了Mina example中的Timer server,它的逻辑非常简单,就是接收到客户端的请求后,返回服务器的当前时间。
TimerServerHandler.java
- public class TimeServerHandler extends IoHandlerAdapter {
-
- @Override
- public void messageReceived(IoSession session, Object message) throws Exception {
- String msg = (String) message;
- if ("quit".equals(msg.trim())) {
- System.out.println("client " + session.getRemoteAddress() + " quited!");
- session.close(false);
- return;
- }
-
- Date date = new Date();
- session.write(date.toString());
- System.out.println("message written...");
- }
-
- }
开启服务器
- private static final int SERVER_PORT = 8888;
-
- public static void main(String[] args) throws IOException {
- IoAcceptor acceptor = new NioSocketAcceptor();
-
- acceptor.getFilterChain().addLast("logging", new LoggingFilter());
- acceptor.getFilterChain().addLast("codec",
- new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
-
- acceptor.setHandler(new TimeServerHandler());
-
- acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
- acceptor.getSessionConfig().setReadBufferSize(2048);
-
- acceptor.bind(new InetSocketAddress(SERVER_PORT));
- }
1、先创建一个IoAcceptor实例,这里创建的是一个基于tcp的Java NIO版的IoAccptor
2、往FilterChain中追加了 LoggingFilter 和TextLineCodecFactory,LoggingFilter 可以对客户端的请求和服务器端的响应日志记录。TextLineCodecFactory 是一个协议编码解码器的工厂,就是将字节流与java中的String之间的相互转换。后面会详细介绍。
3、添加一个TimerServerHandler实例。TimeServerHandler 处理的逻辑就是在接收到客户端发送过来的字符串信息后,判断如果是quit,关闭与客户端的链接。不是的就返回服务器的当前时间的字符串。
4、设置一些IoSession的可配属性
5、绑定到一个端口上,开始监听客户端的请求。
上面我们只写了一个TimerServerHandler,并在messageReceived 方法中定义了自己的业务处理就轻松的完成了一个服务器端的开发,而不用去关心底层的链接和I/O处理,这就是mina的魅力所在,让开发人员从处理容易出错的I/O操作中解放出来。
现在我们逐个分析代码中出现的一些类和方法。先看一个IoService的继承体系。Mina提供了丰富的实现,支持很多协议,IoService的继承体系没有下面简单。简单起见,上面的图中值画出了Server端的结构,分析IoServeric先从IoAcceptor入手,而且就分析我们熟悉的java nio相关的类。
IoAcceptor是IoService在服务器端的一个抽象。先从接口的功能开始分析:
- public interface IoService {
-
- TransportMetadata getTransportMetadata();
-
- void addListener(IoServiceListener listener);
-
- void removeListener(IoServiceListener listener);
-
- boolean isDisposing();
-
- boolean isDisposed();
-
- void dispose();
-
- void dispose(boolean awaitTermination);
-
- IoHandler getHandler();
-
- void setHandler(IoHandler handler);
-
- Map<Long, IoSession> getManagedSessions();
-
- int getManagedSessionCount();
-
- IoSessionConfig getSessionConfig();
-
- IoFilterChainBuilder getFilterChainBuilder();
-
- void setFilterChainBuilder(IoFilterChainBuilder builder);
-
- DefaultIoFilterChainBuilder getFilterChain();
-
- boolean isActive();
-
- long getActivationTime();
-
- Set<WriteFuture> broadcast(Object message);
-
- IoSessionDataStructureFactory getSessionDataStructureFactory();
-
- void setSessionDataStructureFactory(IoSessionDataStructureFactory sessionDataStructureFactory);
-
- int getScheduledWriteBytes();
-
- int getScheduledWriteMessages();
-
- IoServiceStatistics getStatistics();
- }
从接口的方法上分析可以了解到IoService的主要功能:
1、获取链接通信的元数据
2、IoService维护一个IoServiceListener的列表,IoServiceListener顾名思义,就是对IoService相关的事件进行监听。
3、关闭链接
4、一个IoService对应有一个IoHandler
5、IoService维护这一个IoSession的map
6、一个IoService对应一个FilterChain
7、支持广播功能
8、管理IoSession的中的数据结构
9、统计功能
IoAcceptor 在IoService基础上扩展了绑定和解绑SocketAddress的功能。
SocketAcceptor 在IoAcceptor的基础上将SocketAddress 具体化到InetSocketAddress,同时将IoSessionConfig具体化到SocketSessionConfig。提供了reuseaddress 和backlog的设置。关于backlog在SocketServer中的文档描述是
引用
backlog requested maximum length of the queue of incoming connections.
抽象类分析:
AbstractIoService 提供了IoService中的一些默认实现。
- protected AbstractIoService(IoSessionConfig sessionConfig, Executor executor) {
- if (sessionConfig == null) {
- throw new IllegalArgumentException("sessionConfig");
- }
-
- if (getTransportMetadata() == null) {
- throw new IllegalArgumentException("TransportMetadata");
- }
-
- if (!getTransportMetadata().getSessionConfigType().isAssignableFrom(
- sessionConfig.getClass())) {
- throw new IllegalArgumentException("sessionConfig type: "
- + sessionConfig.getClass() + " (expected: "
- + getTransportMetadata().getSessionConfigType() + ")");
- }
-
- listeners = new IoServiceListenerSupport(this);
- listeners.add(serviceActivationListener);
-
- this.sessionConfig = sessionConfig;
-
- ExceptionMonitor.getInstance();
-
- if (executor == null) {
- this.executor = Executors.newCachedThreadPool();
- createdExecutor = true;
- } else {
- this.executor = executor;
- createdExecutor = false;
- }
-
- threadName = getClass().getSimpleName() + '-' + id.incrementAndGet();
- }
从AbstractIoService 构造函数来分析可以得知
1、AbstractIoService中只定义了上面的构成函数,没有显式定义无参构造函数,所以在子类的初始化在肯定会调用super(IoSessionConifg,Executor),再从上面构造函数前面的判断来看,IoSessionConfig,TransportMetadata都是由子类构造函数传入。Executor 参数子类可传可不传,不传默认Executors.newCachedThreadPool();创建。
2、IoServiceListener 列表的管理交给了IoServiceListenerSupport去处理。并添加了一个IoService激活事件的监听器。
3、创建了一个ExceptionMonitor实例
4、定义了构造Acceptor处理线程名称的逻辑
构造函数之外,AbstractIoService 也定义了一些默认实现
1、IoFilterChain 默认交给DefaultIoFilterChainBuilder 创建
2、IoSessionDataStructureFactory 默认实现为 DefaultIoSessionDataStructureFactory
3、实现了dispose的基本逻辑,为什么说是基本逻辑呢?因为dispose调用的 dispose0方法是交由子类去实现的
- public final void dispose(boolean awaitTermination) {
- if (disposed) {
- return;
- }
-
- synchronized (disposalLock) {
- if (!disposing) {
- disposing = true;
-
- try {
- dispose0();
- } catch (Exception e) {
- ExceptionMonitor.getInstance().exceptionCaught(e);
- }
- }
- }
-
- if (createdExecutor) {
- ExecutorService e = (ExecutorService) executor;
- e.shutdownNow();
- if (awaitTermination) {
-
-
-
- try {
- LOGGER.debug("awaitTermination on {} called by thread=[{}]", this, Thread.currentThread().getName());
- e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
- LOGGER.debug("awaitTermination on {} finished", this);
- } catch (InterruptedException e1) {
- LOGGER.warn("awaitTermination on [{}] was interrupted", this);
-
- Thread.currentThread().interrupt();
- }
- }
- }
- disposed = true;
- }
4、实现了broadcast
- public final Set<WriteFuture> broadcast(Object message) {
-
-
-
- final List<WriteFuture> futures = IoUtil.broadcast(message,
- getManagedSessions().values());
- return new AbstractSet<WriteFuture>() {
- @Override
- public Iterator<WriteFuture> iterator() {
- return futures.iterator();
- }
-
- @Override
- public int size() {
- return futures.size();
- }
- };
- }
AbstractIoAcceptor 继承自AbstractIoService并实现了IoAcceptor接口,主要的实现有:
1、增加一个可配置的SocketAddress 列表 defaultLocalAddresses ,以及这个列表的只读版本
2、增加一个已绑定的SocketAddress 列表
3、增加一个disconnectOnUnbind 配置,指定在Unbind时是否断掉与客户端的链接
4、实现了bind的基本逻辑,更具体的逻辑在bindInternal中交由子类去实现
- public final void bind(Iterable<? extends SocketAddress> localAddresses) throws IOException {
- if (isDisposing()) {
- throw new IllegalStateException("Already disposed.");
- }
-
- if (localAddresses == null) {
- throw new IllegalArgumentException("localAddresses");
- }
-
- List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
-
- for (SocketAddress a: localAddresses) {
- checkAddressType(a);
- localAddressesCopy.add(a);
- }
-
- if (localAddressesCopy.isEmpty()) {
- throw new IllegalArgumentException("localAddresses is empty.");
- }
-
- boolean activate = false;
- synchronized (bindLock) {
- synchronized (boundAddresses) {
- if (boundAddresses.isEmpty()) {
- activate = true;
- }
- }
-
- if (getHandler() == null) {
- throw new IllegalStateException("handler is not set.");
- }
-
- try {
- Set<SocketAddress> addresses = bindInternal( localAddressesCopy );
-
- synchronized (boundAddresses) {
- boundAddresses.addAll(addresses);
- }
- } catch (IOException e) {
- throw e;
- } catch (RuntimeException e) {
- throw e;
- } catch (Throwable e) {
- throw new RuntimeIoException(
- "Failed to bind to: " + getLocalAddresses(), e);
- }
- }
-
- if (activate) {
- getListeners().fireServiceActivated();
- }
- }
5、实现了unbind的基本逻辑,更具体的逻辑在unbind0中交由子类实现
- public final void unbind(Iterable<? extends SocketAddress> localAddresses) {
- if (localAddresses == null) {
- throw new IllegalArgumentException("localAddresses");
- }
-
- boolean deactivate = false;
- synchronized (bindLock) {
- synchronized (boundAddresses) {
- if (boundAddresses.isEmpty()) {
- return;
- }
-
- List<SocketAddress> localAddressesCopy = new ArrayList<SocketAddress>();
- int specifiedAddressCount = 0;
-
- for (SocketAddress a: localAddresses ) {
- specifiedAddressCount++;
-
- if ((a != null) && boundAddresses.contains(a) ) {
- localAddressesCopy.add(a);
- }
- }
-
- if (specifiedAddressCount == 0) {
- throw new IllegalArgumentException( "localAddresses is empty." );
- }
-
- if (!localAddressesCopy.isEmpty()) {
- try {
- unbind0(localAddressesCopy);
- } catch (RuntimeException e) {
- throw e;
- } catch (Throwable e) {
- throw new RuntimeIoException(
- "Failed to unbind from: " + getLocalAddresses(), e );
- }
-
- boundAddresses.removeAll(localAddressesCopy);
-
- if (boundAddresses.isEmpty()) {
- deactivate = true;
- }
- }
- }
- }
-
- if (deactivate) {
- getListeners().fireServiceDeactivated();
- }
- }
AbstractPollingIoAcceptor<T extends AbstractIoSession, H>
泛参H在子类NioSocketAcceptor中替换为ServerSocketChannel,泛参T替换为NioSession。这样便于分析后面的代码。
主要实现了bind,accept,dispose ServerSocket的基本逻辑。父类AbstractIoService中的Executor主要用于执行ServerSocket的accept逻辑。一旦与客户端建立连接后,之后的I/O操作将交由IoProcessor去处理。关于ServerSocketChannel 的 select,open,close,accept都交由子类实现
私有构造函数定义了IoProcessor实例由外部注入,并初始化seelectable标记为true。具体的init()逻辑由子类去实现。
- private AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
- Executor executor, IoProcessor<NioSession> processor,
- boolean createdProcessor) {
- super(sessionConfig, executor);
-
- if (processor == null) {
- throw new IllegalArgumentException("processor");
- }
-
- this.processor = processor;
- this.createdProcessor = createdProcessor;
-
- try {
-
- init();
-
- selectable = true;
- } catch (RuntimeException e) {
- throw e;
- } catch (Exception e) {
- throw new RuntimeIoException("Failed to initialize.", e);
- } finally {
- if (!selectable) {
- try {
- destroy();
- } catch (Exception e) {
- ExceptionMonitor.getInstance().exceptionCaught(e);
- }
- }
- }
- }
其他几个重载的构造函数注入默认IoProcessor为SimpleIoProcessorPool实例
- protected AbstractPollingIoAcceptor(IoSessionConfig sessionConfig,
- Class<? extends IoProcessor<T>> processorClass, int processorCount) {
- this(sessionConfig, null, new SimpleIoProcessorPool<T>(processorClass,
- processorCount), true);
- }
提供了一个轮询策略的Acceptor的实现
NioSocketAcceptor
基于tcp协议的java nio版IoAcceptor实现,到这里已经实现所有的网络I/O操作。
-
- private int backlog = 50;
-
- private boolean reuseAddress = false;
-
- private volatile Selector selector;
-
-
- public NioSocketAcceptor() {
- super(new DefaultSocketSessionConfig(), NioProcessor.class);
- ((DefaultSocketSessionConfig) getSessionConfig()).init(this);
- }
-
- @Override
- protected void init() throws Exception {
- selector = Selector.open();
- }
-
- @Override
- protected void destroy() throws Exception {
- if (selector != null) {
- selector.close();
- }
- }
-
- public TransportMetadata getTransportMetadata() {
- return NioSocketSession.METADATA;
- }
-
- @Override
- protected NioSession accept(IoProcessor<NioSession> processor,
- ServerSocketChannel handle) throws Exception {
-
- SelectionKey key = handle.keyFor(selector);
-
- if ((key == null) || (!key.isValid()) || (!key.isAcceptable()) ) {
- return null;
- }
-
- SocketChannel ch = handle.accept();
-
- if (ch == null) {
- return null;
- }
-
- return new NioSocketSession(this, processor, ch);
- }
-
- @Override
- protected ServerSocketChannel open(SocketAddress localAddress)
- throws Exception {
-
- ServerSocketChannel channel = ServerSocketChannel.open();
-
- boolean success = false;
-
- try {
-
- channel.configureBlocking(false);
-
-
- ServerSocket socket = channel.socket();
-
-
- socket.setReuseAddress(isReuseAddress());
-
-
- socket.bind(localAddress, getBacklog());
-
-
- channel.register(selector, SelectionKey.OP_ACCEPT);
- success = true;
- } finally {
- if (!success) {
- close(channel);
- }
- }
- return channel;
- }
-
- @Override
- protected void close(ServerSocketChannel handle) throws Exception {
- SelectionKey key = handle.keyFor(selector);
-
- if (key != null) {
- key.cancel();
- }
-
- handle.close();
- }
-
- @Override
- protected void wakeup() {
- selector.wakeup();
- } http://berdy.iteye.com/blog/1960639
- 转自:http://berdy.iteye.com/blog/1960639