1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.nio; 19 20import java.io.FileDescriptor; 21import java.io.IOException; 22import java.io.InterruptedIOException; 23import java.net.ConnectException; 24import java.net.DatagramPacket; 25import java.net.DatagramSocket; 26import java.net.DatagramSocketImpl; 27import java.net.InetAddress; 28import java.net.InetSocketAddress; 29import java.net.PlainDatagramSocketImpl; 30import java.net.SocketAddress; 31import java.net.SocketException; 32import java.nio.channels.AlreadyConnectedException; 33import java.nio.channels.ClosedChannelException; 34import java.nio.channels.DatagramChannel; 35import java.nio.channels.IllegalBlockingModeException; 36import java.nio.channels.NotYetConnectedException; 37import java.nio.channels.spi.SelectorProvider; 38import java.util.Arrays; 39import libcore.io.ErrnoException; 40import libcore.io.IoBridge; 41import libcore.io.IoUtils; 42import libcore.io.Libcore; 43import libcore.util.EmptyArray; 44 45/* 46 * The default implementation class of java.nio.channels.DatagramChannel. 47 */ 48class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChannel { 49 // The fd to interact with native code 50 private final FileDescriptor fd; 51 52 // Our internal DatagramSocket. 53 private DatagramSocket socket = null; 54 55 // The address to be connected. 56 InetSocketAddress connectAddress = null; 57 58 // local port 59 private int localPort; 60 61 // At first, uninitialized. 62 boolean connected = false; 63 64 // whether the socket is bound 65 boolean isBound = false; 66 67 private final Object readLock = new Object(); 68 private final Object writeLock = new Object(); 69 70 /* 71 * Constructor 72 */ 73 protected DatagramChannelImpl(SelectorProvider selectorProvider) throws IOException { 74 super(selectorProvider); 75 fd = IoBridge.socket(false); 76 } 77 78 /* 79 * for native call 80 */ 81 @SuppressWarnings("unused") 82 private DatagramChannelImpl() { 83 super(SelectorProvider.provider()); 84 fd = new FileDescriptor(); 85 connectAddress = new InetSocketAddress(0); 86 } 87 88 /* 89 * Getting the internal DatagramSocket If we have not the socket, we create 90 * a new one. 91 */ 92 @Override 93 synchronized public DatagramSocket socket() { 94 if (socket == null) { 95 socket = new DatagramSocketAdapter(new PlainDatagramSocketImpl(fd, localPort), this); 96 } 97 return socket; 98 } 99 100 /** 101 * Returns the local address to which the socket is bound. 102 */ 103 InetAddress getLocalAddress() { 104 return IoBridge.getSocketLocalAddress(fd); 105 } 106 107 /** 108 * @see java.nio.channels.DatagramChannel#isConnected() 109 */ 110 @Override 111 synchronized public boolean isConnected() { 112 return connected; 113 } 114 115 /** 116 * @see java.nio.channels.DatagramChannel#connect(java.net.SocketAddress) 117 */ 118 @Override 119 synchronized public DatagramChannel connect(SocketAddress address) throws IOException { 120 // must open 121 checkOpen(); 122 // status must be un-connected. 123 if (connected) { 124 throw new IllegalStateException(); 125 } 126 127 // check the address 128 InetSocketAddress inetSocketAddress = SocketChannelImpl.validateAddress(address); 129 try { 130 begin(); 131 IoBridge.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort()); 132 } catch (ConnectException e) { 133 // ConnectException means connect fail, not exception 134 } finally { 135 end(true); 136 } 137 138 // set the connected address. 139 connectAddress = inetSocketAddress; 140 connected = true; 141 isBound = true; 142 return this; 143 } 144 145 /** 146 * @see java.nio.channels.DatagramChannel#disconnect() 147 */ 148 @Override 149 synchronized public DatagramChannel disconnect() throws IOException { 150 if (!isConnected() || !isOpen()) { 151 return this; 152 } 153 connected = false; 154 connectAddress = null; 155 try { 156 Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0); 157 } catch (ErrnoException errnoException) { 158 throw errnoException.rethrowAsIOException(); 159 } 160 if (socket != null) { 161 socket.disconnect(); 162 } 163 return this; 164 } 165 166 @Override 167 public SocketAddress receive(ByteBuffer target) throws IOException { 168 target.checkWritable(); 169 checkOpen(); 170 171 if (!isBound) { 172 return null; 173 } 174 175 SocketAddress retAddr = null; 176 try { 177 begin(); 178 179 // receive real data packet, (not peek) 180 synchronized (readLock) { 181 boolean loop = isBlocking(); 182 if (!target.isDirect()) { 183 retAddr = receiveImpl(target, loop); 184 } else { 185 retAddr = receiveDirectImpl(target, loop); 186 } 187 } 188 } catch (InterruptedIOException e) { 189 // this line used in Linux 190 return null; 191 } finally { 192 end(retAddr != null); 193 } 194 return retAddr; 195 } 196 197 private SocketAddress receiveImpl(ByteBuffer target, boolean loop) throws IOException { 198 SocketAddress retAddr = null; 199 DatagramPacket receivePacket; 200 int oldposition = target.position(); 201 int received = 0; 202 // TODO: disallow mapped buffers and lose this conditional? 203 if (target.hasArray()) { 204 receivePacket = new DatagramPacket(target.array(), target.position() + target.arrayOffset(), target.remaining()); 205 } else { 206 receivePacket = new DatagramPacket(new byte[target.remaining()], target.remaining()); 207 } 208 do { 209 received = IoBridge.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected()); 210 if (receivePacket != null && receivePacket.getAddress() != null) { 211 if (received > 0) { 212 if (target.hasArray()) { 213 target.position(oldposition + received); 214 } else { 215 // copy the data of received packet 216 target.put(receivePacket.getData(), 0, received); 217 } 218 } 219 retAddr = receivePacket.getSocketAddress(); 220 break; 221 } 222 } while (loop); 223 return retAddr; 224 } 225 226 private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) throws IOException { 227 SocketAddress retAddr = null; 228 DatagramPacket receivePacket = new DatagramPacket(EmptyArray.BYTE, 0); 229 int oldposition = target.position(); 230 int received = 0; 231 do { 232 received = IoBridge.recvfrom(false, fd, target, 0, receivePacket, isConnected()); 233 if (receivePacket != null && receivePacket.getAddress() != null) { 234 // copy the data of received packet 235 if (received > 0) { 236 target.position(oldposition + received); 237 } 238 retAddr = receivePacket.getSocketAddress(); 239 break; 240 } 241 } while (loop); 242 return retAddr; 243 } 244 245 @Override 246 public int send(ByteBuffer source, SocketAddress socketAddress) throws IOException { 247 checkNotNull(source); 248 checkOpen(); 249 250 InetSocketAddress isa = (InetSocketAddress) socketAddress; 251 if (isa.getAddress() == null) { 252 throw new IOException(); 253 } 254 255 if (isConnected() && !connectAddress.equals(isa)) { 256 throw new IllegalArgumentException(); 257 } 258 259 synchronized (writeLock) { 260 int sendCount = 0; 261 try { 262 begin(); 263 int oldPosition = source.position(); 264 sendCount = IoBridge.sendto(fd, source, 0, isa.getAddress(), isa.getPort()); 265 if (sendCount > 0) { 266 source.position(oldPosition + sendCount); 267 } 268 } finally { 269 end(sendCount >= 0); 270 } 271 return sendCount; 272 } 273 } 274 275 @Override 276 public int read(ByteBuffer target) throws IOException { 277 target.checkWritable(); 278 checkOpenConnected(); 279 280 if (!target.hasRemaining()) { 281 return 0; 282 } 283 284 int readCount = 0; 285 if (target.isDirect() || target.hasArray()) { 286 readCount = readImpl(target); 287 if (readCount > 0) { 288 target.position(target.position() + readCount); 289 } 290 291 } else { 292 byte[] readArray = new byte[target.remaining()]; 293 ByteBuffer readBuffer = ByteBuffer.wrap(readArray); 294 readCount = readImpl(readBuffer); 295 if (readCount > 0) { 296 target.put(readArray, 0, readCount); 297 } 298 } 299 return readCount; 300 } 301 302 @Override 303 public long read(ByteBuffer[] targets, int offset, int length) throws IOException { 304 Arrays.checkOffsetAndCount(targets.length, offset, length); 305 306 // status must be open and connected 307 checkOpenConnected(); 308 int totalCount = FileChannelImpl.calculateTotalRemaining(targets, offset, length, true); 309 if (totalCount == 0) { 310 return 0; 311 } 312 313 // read data to readBuffer, and then transfer data from readBuffer to 314 // targets. 315 ByteBuffer readBuffer = ByteBuffer.allocate(totalCount); 316 int readCount; 317 readCount = readImpl(readBuffer); 318 int left = readCount; 319 int index = offset; 320 // transfer data from readBuffer to targets 321 byte[] readArray = readBuffer.array(); 322 while (left > 0) { 323 int putLength = Math.min(targets[index].remaining(), left); 324 targets[index].put(readArray, readCount - left, putLength); 325 index++; 326 left -= putLength; 327 } 328 return readCount; 329 } 330 331 /* 332 * read from channel, and store the result in the target. 333 */ 334 private int readImpl(ByteBuffer dst) throws IOException { 335 synchronized (readLock) { 336 int readCount = 0; 337 try { 338 begin(); 339 readCount = IoBridge.recvfrom(false, fd, dst, 0, null, isConnected()); 340 } catch (InterruptedIOException e) { 341 // InterruptedIOException will be thrown when timeout. 342 return 0; 343 } finally { 344 end(readCount > 0); 345 } 346 return readCount; 347 } 348 } 349 350 @Override public int write(ByteBuffer src) throws IOException { 351 checkNotNull(src); 352 checkOpenConnected(); 353 if (!src.hasRemaining()) { 354 return 0; 355 } 356 357 int writeCount = writeImpl(src); 358 if (writeCount > 0) { 359 src.position(src.position() + writeCount); 360 } 361 return writeCount; 362 } 363 364 /** 365 * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer[], int, 366 * int) 367 */ 368 @Override 369 public long write(ByteBuffer[] sources, int offset, int length) throws IOException { 370 Arrays.checkOffsetAndCount(sources.length, offset, length); 371 372 // status must be open and connected 373 checkOpenConnected(); 374 int count = FileChannelImpl.calculateTotalRemaining(sources, offset, length, false); 375 if (count == 0) { 376 return 0; 377 } 378 ByteBuffer writeBuf = ByteBuffer.allocate(count); 379 for (int val = offset; val < length + offset; val++) { 380 ByteBuffer source = sources[val]; 381 int oldPosition = source.position(); 382 writeBuf.put(source); 383 source.position(oldPosition); 384 } 385 writeBuf.flip(); 386 int result = writeImpl(writeBuf); 387 int val = offset; 388 int written = result; 389 while (result > 0) { 390 ByteBuffer source = sources[val]; 391 int gap = Math.min(result, source.remaining()); 392 source.position(source.position() + gap); 393 val++; 394 result -= gap; 395 } 396 return written; 397 } 398 399 private int writeImpl(ByteBuffer buf) throws IOException { 400 synchronized (writeLock) { 401 int result = 0; 402 try { 403 begin(); 404 result = IoBridge.sendto(fd, buf, 0, null, 0); 405 } finally { 406 end(result > 0); 407 } 408 return result; 409 } 410 } 411 412 @Override protected synchronized void implCloseSelectableChannel() throws IOException { 413 connected = false; 414 if (socket != null && !socket.isClosed()) { 415 socket.close(); 416 } else { 417 IoBridge.closeSocket(fd); 418 } 419 } 420 421 @Override protected void implConfigureBlocking(boolean blocking) throws IOException { 422 synchronized (blockingLock()) { 423 IoUtils.setBlocking(fd, blocking); 424 } 425 } 426 427 /* 428 * Status check, must be open. 429 */ 430 private void checkOpen() throws IOException { 431 if (!isOpen()) { 432 throw new ClosedChannelException(); 433 } 434 } 435 436 /* 437 * Status check, must be open and connected, for read and write. 438 */ 439 private void checkOpenConnected() throws IOException { 440 checkOpen(); 441 if (!isConnected()) { 442 throw new NotYetConnectedException(); 443 } 444 } 445 446 /* 447 * Buffer check, must not null 448 */ 449 private void checkNotNull(ByteBuffer source) { 450 if (source == null) { 451 throw new NullPointerException("source == null"); 452 } 453 } 454 455 /* 456 * Get the fd for internal use. 457 */ 458 public FileDescriptor getFD() { 459 return fd; 460 } 461 462 /* 463 * The adapter class of DatagramSocket 464 */ 465 private static class DatagramSocketAdapter extends DatagramSocket { 466 467 /* 468 * The internal datagramChannelImpl. 469 */ 470 private DatagramChannelImpl channelImpl; 471 472 /* 473 * Constructor initialize the datagramSocketImpl and datagramChannelImpl 474 */ 475 DatagramSocketAdapter(DatagramSocketImpl socketimpl, 476 DatagramChannelImpl channelImpl) { 477 super(socketimpl); 478 this.channelImpl = channelImpl; 479 } 480 481 /* 482 * Get the internal datagramChannelImpl 483 */ 484 @Override 485 public DatagramChannel getChannel() { 486 return channelImpl; 487 } 488 489 /** 490 * @see java.net.DatagramSocket#isBound() 491 */ 492 @Override 493 public boolean isBound() { 494 return channelImpl.isBound; 495 } 496 497 /** 498 * @see java.net.DatagramSocket#isConnected() 499 */ 500 @Override 501 public boolean isConnected() { 502 return channelImpl.isConnected(); 503 } 504 505 /** 506 * @see java.net.DatagramSocket#getInetAddress() 507 */ 508 @Override 509 public InetAddress getInetAddress() { 510 if (channelImpl.connectAddress == null) { 511 return null; 512 } 513 return channelImpl.connectAddress.getAddress(); 514 } 515 516 /** 517 * @see java.net.DatagramSocket#getLocalAddress() 518 */ 519 @Override 520 public InetAddress getLocalAddress() { 521 return channelImpl.getLocalAddress(); 522 } 523 524 /** 525 * @see java.net.DatagramSocket#getPort() 526 */ 527 @Override 528 public int getPort() { 529 if (channelImpl.connectAddress == null) { 530 return -1; 531 } 532 return channelImpl.connectAddress.getPort(); 533 } 534 535 /** 536 * @see java.net.DatagramSocket#bind(java.net.SocketAddress) 537 */ 538 @Override 539 public void bind(SocketAddress localAddr) throws SocketException { 540 if (channelImpl.isConnected()) { 541 throw new AlreadyConnectedException(); 542 } 543 super.bind(localAddr); 544 channelImpl.isBound = true; 545 } 546 547 /** 548 * @see java.net.DatagramSocket#receive(java.net.DatagramPacket) 549 */ 550 @Override 551 public void receive(DatagramPacket packet) throws IOException { 552 if (!channelImpl.isBlocking()) { 553 throw new IllegalBlockingModeException(); 554 } 555 super.receive(packet); 556 } 557 558 /** 559 * @see java.net.DatagramSocket#send(java.net.DatagramPacket) 560 */ 561 @Override 562 public void send(DatagramPacket packet) throws IOException { 563 if (!channelImpl.isBlocking()) { 564 throw new IllegalBlockingModeException(); 565 } 566 super.send(packet); 567 } 568 569 /** 570 * @see java.net.DatagramSocket#close() 571 */ 572 @Override 573 public void close() { 574 synchronized (channelImpl) { 575 if (channelImpl.isOpen()) { 576 try { 577 channelImpl.close(); 578 } catch (IOException e) { 579 // Ignore 580 } 581 } 582 super.close(); 583 } 584 } 585 586 /** 587 * @see java.net.DatagramSocket#disconnect() 588 */ 589 @Override 590 public void disconnect() { 591 try { 592 channelImpl.disconnect(); 593 } catch (IOException e) { 594 // Ignore 595 } 596 super.disconnect(); 597 } 598 } 599} 600