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 }