View Javadoc

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
    10   *    http://www.apache.org/licenses/LICENSE-2.0
    11   *
    12   *  Unless required by applicable law or agreed to in writing,
    13   *  software distributed under the License is distributed on an
    14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    15   *  KIND, either express or implied.  See the License for the
    16   *  specific language governing permissions and limitations
    17   *  under the License.
    18   *
    19   */
    20  package org.apache.mina.transport.bio;
    21
    22  import java.io.IOException;
    23  import java.net.InetSocketAddress;
    24  import java.net.SocketAddress;
    25  import java.nio.ByteBuffer;
    26  import java.nio.channels.AsynchronousCloseException;
    27  import java.nio.channels.DatagramChannel;
    28  import java.util.Map;
    29  import java.util.concurrent.ConcurrentHashMap;
    30
    31  import org.apache.mina.api.IdleStatus;
    32  import org.apache.mina.api.IoFuture;
    33  import org.apache.mina.api.IoSession;
    34  import org.apache.mina.api.MinaRuntimeException;
    35  import org.apache.mina.service.executor.IoHandlerExecutor;
    36  import org.apache.mina.service.idlechecker.IdleChecker;
    37  import org.apache.mina.service.idlechecker.IndexedIdleChecker;
    38  import org.apache.mina.transport.ConnectFuture;
    39  import org.apache.mina.transport.udp.AbstractUdpServer;
    40  import org.apache.mina.transport.udp.UdpSessionConfig;
    41  import org.slf4j.Logger;
    42  import org.slf4j.LoggerFactory;
    43
    44  /**
    45   * A java blocking I/O based UDP server.
    46   *
    47   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
    48   *
    49   */
    50  public class BioUdpServer extends AbstractUdpServer {
    51
    52      private static final Logger LOG = LoggerFactory.getLogger(BioUdpServer.class);
    53
    54      private SocketAddress boundAddress = null;
    55
    56      // the inner server socket/channel
    57      DatagramChannel channel;
    58
    59      // thread in charge of reading the server socket
    60      private Worker worker;
    61      private boolean bound = false;
    62
    63      // for detecting idle sessions
    64      private IdleChecker idleChecker = new IndexedIdleChecker();
    65
    66      // list of all the sessions by remote socket address
    67      private final Map<SocketAddress /* remote socket address */, BioUdpSession> sessions = new ConcurrentHashMap<SocketAddress, BioUdpSession>();
    68
    69      /**
    70       * Create an UDP server
    71       */
    72      public BioUdpServer() {
    73          super(null);
    74      }
    75
    76      public BioUdpServer(IoHandlerExecutor executor) {
    77          super(executor);
    78      }
    79
    80      public BioUdpServer(UdpSessionConfig config, IoHandlerExecutor executor) {
    81          super(config, executor);
    82      }
    83
    84      public DatagramChannel getDatagramChannel() {
    85          return channel;
    86      }
    87
    88      @Override
    89      public SocketAddress getBoundAddress() {
    90          return boundAddress;
    91      }
    92
    93      @Override
    94      public void bind(SocketAddress localAddress) {
    95          LOG.info("binding to address {}", localAddress);
    96          if (bound) {
    97              throw new IllegalStateException("already bound");
    98          }
    99          boundAddress = localAddress;
    100         try {
    101             channel = DatagramChannel.open();
    102             channel.socket().bind(localAddress);
    103             Boolean reuseAddress = config.isReuseAddress();
    104
    105             if (reuseAddress != null) {
    106                 channel.socket().setReuseAddress(reuseAddress);
    107             }
    108
    109             Integer readBufferSize = config.getReadBufferSize();
    110
    111             if (readBufferSize != null) {
    112                 channel.socket().setReceiveBufferSize(readBufferSize);
    113             }
    114
    115             Integer sendBufferSize = config.getSendBufferSize();
    116
    117             if (sendBufferSize != null) {
    118                 channel.socket().setSendBufferSize(sendBufferSize);
    119             }
    120
    121             Integer trafficClass = config.getTrafficClass();
    122
    123             if (trafficClass != null) {
    124                 channel.socket().setTrafficClass(trafficClass);
    125             }
    126         } catch (IOException e) {
    127             throw new MinaRuntimeException("Can't open and configure a new udp socket", e);
    128         }
    129
    130         worker = new Worker();
    131         bound = true;
    132         worker.start();
    133         idleChecker.start();
    134     }
    135
    136     @Override
    137     public void bind(int port) {
    138         bind(new InetSocketAddress(port));
    139     }
    140
    141     @Override
    142     public void unbind() {
    143         if (!bound) {
    144             throw new IllegalStateException("not bound");
    145         }
    146         bound = false;
    147         try {
    148             try {
    149                 channel.close();
    150             } catch (IOException e) {
    151                 throw new MinaRuntimeException("can't unbind the udp channel", e);
    152             }
    153             boundAddress = null;
    154             idleChecker.destroy();
    155             worker.join();
    156         } catch (InterruptedException e) {
    157             LOG.error("exception", e);
    158         }
    159
    160     }
    161
    162     @Override
    163     public IoFuture<IoSession> connect(SocketAddress remoteAddress) {
    164         BioUdpSession session = new BioUdpSession(remoteAddress, BioUdpServer.this, idleChecker);
    165         sessions.put(remoteAddress, session);
    166         ConnectFuture cf = new ConnectFuture();
    167         cf.complete(session);
    168         return cf;
    169     }
    170
    171     private class Worker extends Thread {
    172
    173         public Worker() {
    174             super("BioUdpServerWorker");
    175         }
    176
    177         @Override
    178         public void run() {
    179             /** 64k receive buffer */
    180             ByteBuffer rcvdBuffer = ByteBuffer.allocate(64 * 1024);
    181             while (bound) {
    182                 // I/O !
    183                 rcvdBuffer.clear();
    184                 try {
    185                     SocketAddress from = channel.receive(rcvdBuffer);
    186                     BioUdpSession session = sessions.get(from);
    187                     if (session == null) {
    188                         // create the session
    189                         session = new BioUdpSession(from, BioUdpServer.this, idleChecker);
    190                         sessions.put(from, session);
    191                         session.getConfig().setIdleTimeInMillis(IdleStatus.READ_IDLE,
    192                                 config.getIdleTimeInMillis(IdleStatus.READ_IDLE));
    193                         session.getConfig().setIdleTimeInMillis(IdleStatus.WRITE_IDLE,
    194                                 config.getIdleTimeInMillis(IdleStatus.WRITE_IDLE));
    195                         idleChecker.sessionWritten(session, System.currentTimeMillis());
    196                         // fire open
    197                         session.processSessionOpen();
    198
    199                     }
    200                     rcvdBuffer.flip();
    201                     session.processMessageReceived(rcvdBuffer);
    202                     // Update the session idle status
    203                     idleChecker.sessionRead(session, System.currentTimeMillis());
    204                 } catch (AsynchronousCloseException aec) {
    205                     LOG.debug("closed service");
    206                     break;
    207                 } catch (IOException e) {
    208                     LOG.error("Exception while reading", e);
    209                 }
    210             }
    211         }
    212     }
    213 }