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 18 package org.apache.harmony.nio.internal; 19 20 // BEGIN android-note 21 // In this class the address length was changed from long to int. 22 // END android-note 23 24 import java.io.FileDescriptor; 25 import java.io.IOException; 26 import java.io.InterruptedIOException; 27 import java.net.ConnectException; 28 import java.net.DatagramPacket; 29 import java.net.DatagramSocket; 30 import java.net.DatagramSocketImpl; 31 import java.net.InetAddress; 32 import java.net.InetSocketAddress; 33 import java.net.SocketAddress; 34 import java.net.SocketException; 35 import java.nio.ByteBuffer; 36 import java.nio.channels.AlreadyConnectedException; 37 import java.nio.channels.ClosedChannelException; 38 import java.nio.channels.DatagramChannel; 39 import java.nio.channels.IllegalBlockingModeException; 40 import java.nio.channels.NotYetConnectedException; 41 import java.nio.channels.spi.SelectorProvider; 42 43 import org.apache.harmony.luni.net.PlainDatagramSocketImpl; 44 import org.apache.harmony.luni.platform.FileDescriptorHandler; 45 import org.apache.harmony.luni.platform.INetworkSystem; 46 import org.apache.harmony.luni.platform.Platform; 47 //import org.apache.harmony.luni.util.ErrorCodeException; android-removed 48 import org.apache.harmony.nio.AddressUtil; 49 50 /* 51 * The default implementation class of java.nio.channels.DatagramChannel. 52 */ 53 class DatagramChannelImpl extends DatagramChannel implements 54 FileDescriptorHandler { 55 56 // The singleton to do the native network operation. 57 private static final INetworkSystem networkSystem = Platform 58 .getNetworkSystem(); 59 60 // default timeout used to nonblocking mode. 61 private static final int DEFAULT_TIMEOUT = 1; 62 63 // android-removed: private static final int ERRCODE_SOCKET_NONBLOCKING_WOULD_BLOCK = -211; 64 65 private static final byte[] stubArray = new byte[0]; 66 67 // The fd to interact with native code 68 private FileDescriptor fd; 69 70 // Our internal DatagramSocket. 71 private DatagramSocket socket = null; 72 73 // The address to be connected. 74 InetSocketAddress connectAddress = null; 75 76 // local port 77 private int localPort; 78 79 // At first, uninitialized. 80 boolean connected = false; 81 82 // whether the socket is bound 83 boolean isBound = false; 84 85 private static class ReadLock {} 86 private final Object readLock = new ReadLock(); 87 88 private static class WriteLock {} 89 private final Object writeLock = new WriteLock(); 90 91 // used to store the trafficClass value which is simply returned 92 // as the value that was set. We also need it to pass it to methods 93 // that specify an address packets are going to be sent to 94 private int trafficClass = 0; 95 96 /* 97 * Constructor 98 */ 99 protected DatagramChannelImpl(SelectorProvider selectorProvider) 100 throws IOException { 101 super(selectorProvider); 102 fd = new FileDescriptor(); 103 networkSystem.createDatagramSocket(fd, true); 104 } 105 106 /* 107 * for native call 108 */ 109 @SuppressWarnings("unused") 110 private DatagramChannelImpl() { 111 super(SelectorProvider.provider()); 112 fd = new FileDescriptor(); 113 connectAddress = new InetSocketAddress(0); 114 } 115 116 /* 117 * Getting the internal DatagramSocket If we have not the socket, we create 118 * a new one. 119 */ 120 @Override 121 synchronized public DatagramSocket socket() { 122 if (null == socket) { 123 socket = new DatagramSocketAdapter( 124 new PlainDatagramSocketImpl(fd, localPort), this); 125 } 126 return socket; 127 } 128 129 /** 130 * Answer the local address from the IP stack. This method should not be 131 * called directly as it does not check the security policy. 132 * 133 * @return InetAddress the local address to which the socket is bound. 134 * @see DatagramSocket 135 */ 136 InetAddress getLocalAddress() { 137 return networkSystem.getSocketLocalAddress(fd); 138 } 139 140 /** 141 * @see java.nio.channels.DatagramChannel#isConnected() 142 */ 143 @Override 144 synchronized public boolean isConnected() { 145 return connected; 146 } 147 148 /** 149 * @see java.nio.channels.DatagramChannel#connect(java.net.SocketAddress) 150 */ 151 @Override 152 synchronized public DatagramChannel connect(SocketAddress address) 153 throws IOException { 154 // must open 155 checkOpen(); 156 // status must be un-connected. 157 if (connected) { 158 throw new IllegalStateException(); 159 } 160 161 // check the address 162 InetSocketAddress inetSocketAddress = SocketChannelImpl 163 .validateAddress(address); 164 165 // security check 166 SecurityManager sm = System.getSecurityManager(); 167 if (null != sm) { 168 if (inetSocketAddress.getAddress().isMulticastAddress()) { 169 sm.checkMulticast(inetSocketAddress.getAddress()); 170 } else { 171 sm.checkConnect(inetSocketAddress.getAddress().getHostName(), 172 inetSocketAddress.getPort()); 173 } 174 } 175 176 try { 177 begin(); 178 networkSystem.connectDatagram(fd, inetSocketAddress.getPort(), 179 trafficClass, inetSocketAddress.getAddress()); 180 } catch (ConnectException e) { 181 // ConnectException means connect fail, not exception 182 } finally { 183 end(true); 184 } 185 186 // set the connected address. 187 connectAddress = inetSocketAddress; 188 connected = true; 189 isBound = true; 190 return this; 191 } 192 193 /** 194 * @see java.nio.channels.DatagramChannel#disconnect() 195 */ 196 @Override 197 synchronized public DatagramChannel disconnect() throws IOException { 198 if (!isConnected() || !isOpen()) { 199 return this; 200 } 201 connected = false; 202 connectAddress = null; 203 networkSystem.disconnectDatagram(fd); 204 if (null != socket) { 205 socket.disconnect(); 206 } 207 return this; 208 } 209 210 /** 211 * @see java.nio.channels.DatagramChannel#receive(java.nio.ByteBuffer) 212 */ 213 @Override 214 public SocketAddress receive(ByteBuffer target) throws IOException { 215 // must not null and not readonly 216 checkWritable(target); 217 // must open 218 checkOpen(); 219 220 if (!isBound) { 221 return null; 222 } 223 224 SocketAddress retAddr = null; 225 try { 226 begin(); 227 228 // receive real data packet, (not peek) 229 synchronized (readLock) { 230 boolean loop = isBlocking(); 231 if (!target.isDirect()) { 232 retAddr = receiveImpl(target, loop); 233 } else { 234 retAddr = receiveDirectImpl(target, loop); 235 } 236 } 237 } catch (InterruptedIOException e) { 238 // this line used in Linux 239 return null; 240 } finally { 241 end(null != retAddr); 242 } 243 return retAddr; 244 } 245 246 private SocketAddress receiveImpl(ByteBuffer target, boolean loop) 247 throws IOException { 248 SocketAddress retAddr = null; 249 DatagramPacket receivePacket; 250 int oldposition = target.position(); 251 int received = 0; 252 if (target.hasArray()) { 253 receivePacket = new DatagramPacket(target.array(), target 254 .position() 255 + target.arrayOffset(), target.remaining()); 256 } else { 257 receivePacket = new DatagramPacket(new byte[target.remaining()], 258 target.remaining()); 259 } 260 do { 261 if (isConnected()) { 262 received = networkSystem.recvConnectedDatagram(fd, 263 receivePacket, receivePacket.getData(), receivePacket 264 .getOffset(), receivePacket.getLength(), 265 isBlocking() ? 0 : DEFAULT_TIMEOUT, false); 266 } else { 267 received = networkSystem.receiveDatagram(fd, receivePacket, 268 receivePacket.getData(), receivePacket.getOffset(), 269 receivePacket.getLength(), isBlocking() ? 0 270 : DEFAULT_TIMEOUT, false); 271 } 272 273 // security check 274 SecurityManager sm = System.getSecurityManager(); 275 if (!isConnected() && null != sm) { 276 try { 277 sm.checkAccept(receivePacket.getAddress().getHostAddress(), 278 receivePacket.getPort()); 279 } catch (SecurityException e) { 280 // do discard the datagram packet 281 receivePacket = null; 282 } 283 } 284 if (null != receivePacket && null != receivePacket.getAddress()) { 285 286 if (received > 0) { 287 if (target.hasArray()) { 288 target.position(oldposition + received); 289 } else { 290 // copy the data of received packet 291 target.put(receivePacket.getData(), 0, received); 292 } 293 } 294 retAddr = receivePacket.getSocketAddress(); 295 break; 296 } 297 } while (loop); 298 return retAddr; 299 } 300 301 private SocketAddress receiveDirectImpl(ByteBuffer target, boolean loop) 302 throws IOException { 303 SocketAddress retAddr = null; 304 DatagramPacket receivePacket = new DatagramPacket(stubArray, 0); 305 int oldposition = target.position(); 306 int received = 0; 307 do { 308 int address = AddressUtil.getDirectBufferAddress(target); 309 if (isConnected()) { 310 received = networkSystem.recvConnectedDatagramDirect(fd, 311 receivePacket, address, target.position(), target 312 .remaining(), isBlocking() ? 0 313 : DEFAULT_TIMEOUT, false); 314 } else { 315 received = networkSystem.receiveDatagramDirect(fd, 316 receivePacket, address, target.position(), target 317 .remaining(), isBlocking() ? 0 318 : DEFAULT_TIMEOUT, false); 319 } 320 321 // security check 322 SecurityManager sm = System.getSecurityManager(); 323 if (!isConnected() && null != sm) { 324 try { 325 sm.checkAccept(receivePacket.getAddress().getHostAddress(), 326 receivePacket.getPort()); 327 } catch (SecurityException e) { 328 // do discard the datagram packet 329 receivePacket = null; 330 } 331 } 332 if (null != receivePacket && null != receivePacket.getAddress()) { 333 // copy the data of received packet 334 if (received > 0) { 335 target.position(oldposition + received); 336 } 337 retAddr = receivePacket.getSocketAddress(); 338 break; 339 } 340 } while (loop); 341 return retAddr; 342 } 343 344 /** 345 * @see java.nio.channels.DatagramChannel#send(java.nio.ByteBuffer, 346 * java.net.SocketAddress) 347 */ 348 @Override 349 public int send(ByteBuffer source, SocketAddress socketAddress) 350 throws IOException { 351 // must not null 352 checkNotNull(source); 353 // must open 354 checkOpen(); 355 356 // transfer socketAddress 357 InetSocketAddress isa = (InetSocketAddress) socketAddress; 358 if (null == isa.getAddress()) { 359 throw new IOException(); 360 } 361 362 if (isConnected()) { 363 if (!connectAddress.equals(isa)) { 364 throw new IllegalArgumentException(); 365 } 366 } else { 367 // not connected, check security 368 SecurityManager sm = System.getSecurityManager(); 369 if (sm != null) { 370 if (isa.getAddress().isMulticastAddress()) { 371 sm.checkMulticast(isa.getAddress()); 372 } else { 373 sm.checkConnect(isa.getAddress().getHostAddress(), isa 374 .getPort()); 375 } 376 } 377 } 378 379 // the return value. 380 int sendCount = 0; 381 try { 382 begin(); 383 byte[] array = null; 384 int length = source.remaining(); 385 int oldposition = source.position(); 386 int start = oldposition; 387 if (source.isDirect()) { 388 synchronized (writeLock) { 389 int data_address = AddressUtil 390 .getDirectBufferAddress(source); 391 sendCount = networkSystem.sendDatagramDirect(fd, 392 data_address, start, length, isa.getPort(), false, 393 trafficClass, isa.getAddress()); 394 } 395 } else { 396 if (source.hasArray()) { 397 array = source.array(); 398 start += source.arrayOffset(); 399 } else { 400 array = new byte[length]; 401 source.get(array); 402 start = 0; 403 } 404 synchronized (writeLock) { 405 sendCount = networkSystem.sendDatagram(fd, array, start, 406 length, isa.getPort(), false, trafficClass, isa 407 .getAddress()); 408 } 409 } 410 source.position(oldposition + sendCount); 411 return sendCount; 412 } finally { 413 end(sendCount >= 0); 414 } 415 } 416 417 /** 418 * @see java.nio.channels.DatagramChannel#read(java.nio.ByteBuffer) 419 */ 420 @Override 421 public int read(ByteBuffer target) throws IOException { 422 if (null == target) { 423 throw new NullPointerException(); 424 } 425 // status must be open and connected 426 checkOpenConnected(); 427 // target buffer must be not null and not readonly 428 checkWritable(target); 429 430 if (!target.hasRemaining()) { 431 return 0; 432 } 433 434 int readCount = 0; 435 if (target.isDirect() || target.hasArray()) { 436 readCount = readImpl(target); 437 if (readCount > 0) { 438 target.position(target.position() + readCount); 439 } 440 441 } else { 442 byte[] readArray = new byte[target.remaining()]; 443 ByteBuffer readBuffer = ByteBuffer.wrap(readArray); 444 readCount = readImpl(readBuffer); 445 if (readCount > 0) { 446 target.put(readArray, 0, readCount); 447 } 448 } 449 return readCount; 450 } 451 452 /** 453 * @see java.nio.channels.DatagramChannel#read(java.nio.ByteBuffer[], int, 454 * int) 455 */ 456 @Override 457 public long read(ByteBuffer[] targets, int offset, int length) 458 throws IOException { 459 if (length < 0 || offset < 0 460 || (long) length + (long) offset > targets.length) { 461 throw new IndexOutOfBoundsException(); 462 } 463 464 // status must be open and connected 465 checkOpenConnected(); 466 467 int totalCount = 0; 468 for (int val = offset; val < length; val++) { 469 // target buffer must be not null and not readonly 470 checkWritable(targets[val]); 471 totalCount += targets[val].remaining(); 472 } 473 474 // read data to readBuffer, and then transfer data from readBuffer to 475 // targets. 476 ByteBuffer readBuffer = ByteBuffer.allocate(totalCount); 477 int readCount; 478 readCount = readImpl(readBuffer); 479 int left = readCount; 480 int index = offset; 481 // transfer data from readBuffer to targets 482 byte[] readArray = readBuffer.array(); 483 while (left > 0) { 484 int putLength = Math.min(targets[index].remaining(), left); 485 targets[index].put(readArray, readCount - left, putLength); 486 index++; 487 left -= putLength; 488 } 489 return readCount; 490 } 491 492 /* 493 * read from channel, and store the result in the target. 494 */ 495 private int readImpl(ByteBuffer readBuffer) throws IOException { 496 synchronized (readLock) { 497 int readCount = 0; 498 try { 499 begin(); 500 // timeout == 0 means block read. 501 // DEFAULT_TIMEOUT is used in non-block mode. 502 int timeout = isBlocking() ? 0 : DEFAULT_TIMEOUT; 503 int start = readBuffer.position(); 504 int length = readBuffer.remaining(); 505 if (readBuffer.isDirect()) { 506 int address = AddressUtil.getDirectBufferAddress(readBuffer); 507 if (isConnected()) { 508 readCount = networkSystem.recvConnectedDatagramDirect( 509 fd, null, address, start, length, timeout, 510 false); 511 } else { 512 readCount = networkSystem.receiveDatagramDirect(fd, 513 null, address, start, length, timeout, false); 514 } 515 } else { 516 // the target is assured to have array. 517 byte[] target = readBuffer.array(); 518 start += readBuffer.arrayOffset(); 519 if (isConnected()) { 520 readCount = networkSystem.recvConnectedDatagram(fd, 521 null, target, start, length, timeout, false); 522 } else { 523 readCount = networkSystem.receiveDatagram(fd, null, 524 target, start, length, timeout, false); 525 } 526 } 527 return readCount; 528 } catch (InterruptedIOException e) { 529 // InterruptedIOException will be thrown when timeout. 530 return 0; 531 } finally { 532 end(readCount > 0); 533 } 534 } 535 } 536 537 /** 538 * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer) 539 */ 540 @Override 541 public int write(ByteBuffer source) throws IOException { 542 // source buffer must be not null 543 checkNotNull(source); 544 // status must be open and connected 545 checkOpenConnected(); 546 // return immediately if source is full 547 if (!source.hasRemaining()) { 548 return 0; 549 } 550 551 ByteBuffer writeBuffer = null; 552 byte[] writeArray = null; 553 int oldposition = source.position(); 554 int result; 555 if (source.isDirect() || source.hasArray()) { 556 writeBuffer = source; 557 } else { 558 writeArray = new byte[source.remaining()]; 559 source.get(writeArray); 560 writeBuffer = ByteBuffer.wrap(writeArray); 561 } 562 result = writeImpl(writeBuffer); 563 if (result > 0) { 564 source.position(oldposition + result); 565 } 566 return result; 567 } 568 569 /** 570 * @see java.nio.channels.DatagramChannel#write(java.nio.ByteBuffer[], int, 571 * int) 572 */ 573 @Override 574 public long write(ByteBuffer[] sources, int offset, int length) 575 throws IOException { 576 if (length < 0 || offset < 0 577 || (long) length + (long) offset > sources.length) { 578 throw new IndexOutOfBoundsException(); 579 } 580 581 // status must be open and connected 582 checkOpenConnected(); 583 int count = calculateByteBufferArray(sources, offset, length); 584 if (0 == count) { 585 return 0; 586 } 587 ByteBuffer writeBuf = ByteBuffer.allocate(count); 588 for (int val = offset; val < length + offset; val++) { 589 ByteBuffer source = sources[val]; 590 int oldPosition = source.position(); 591 writeBuf.put(source); 592 source.position(oldPosition); 593 } 594 writeBuf.flip(); 595 int result = writeImpl(writeBuf); 596 int val = offset; 597 int written = result; 598 while (result > 0) { 599 ByteBuffer source = sources[val]; 600 int gap = Math.min(result, source.remaining()); 601 source.position(source.position() + gap); 602 val++; 603 result -= gap; 604 } 605 return written; 606 } 607 608 /* 609 * Write the source. Return the count of bytes written. 610 */ 611 private int writeImpl(ByteBuffer buf) throws IOException { 612 synchronized (writeLock) { 613 int result = 0; 614 try { 615 begin(); 616 int length = buf.remaining(); 617 int start = buf.position(); 618 619 if (buf.isDirect()) { 620 int address = AddressUtil.getDirectBufferAddress(buf); 621 result = networkSystem.sendConnectedDatagramDirect(fd, 622 address, start, length, isBound); 623 } else { 624 // buf is assured to have array. 625 start += buf.arrayOffset(); 626 result = networkSystem.sendConnectedDatagram(fd, buf 627 .array(), start, length, isBound); 628 } 629 return result; 630 // android-removed: bogus catch (SocketException e) and use of ErrorCodeException. 631 } finally { 632 end(result > 0); 633 } 634 } 635 } 636 637 /* 638 * Do really closing action here. 639 */ 640 @Override 641 synchronized protected void implCloseSelectableChannel() throws IOException { 642 connected = false; 643 if (null != socket && !socket.isClosed()) { 644 socket.close(); 645 } else { 646 networkSystem.socketClose(fd); 647 } 648 } 649 650 /** 651 * @see java.nio.channels.spi.AbstractSelectableChannel#implConfigureBlocking(boolean) 652 */ 653 @Override 654 @SuppressWarnings("unused") 655 protected void implConfigureBlocking(boolean blockingMode) 656 throws IOException { 657 // Do nothing here. For real read/write operation in nonblocking mode, 658 // it uses select system call. Whether a channel is blocking can be 659 // decided by isBlocking() method. 660 } 661 662 /* 663 * Status check, must be open. 664 */ 665 private void checkOpen() throws IOException { 666 if (!isOpen()) { 667 throw new ClosedChannelException(); 668 } 669 } 670 671 /* 672 * Status check, must be open and connected, for read and write. 673 */ 674 private void checkOpenConnected() throws IOException { 675 checkOpen(); 676 if (!isConnected()) { 677 throw new NotYetConnectedException(); 678 } 679 } 680 681 /* 682 * Buffer check, must not null 683 */ 684 private void checkNotNull(ByteBuffer source) { 685 if (null == source) { 686 throw new NullPointerException(); 687 } 688 } 689 690 /* 691 * Buffer check, must not null and not read only buffer, for read and 692 * receive. 693 */ 694 private void checkWritable(ByteBuffer target) { 695 // including checking of NPE. 696 if (target.isReadOnly()) { 697 throw new IllegalArgumentException(); 698 } 699 } 700 701 /* 702 * Get the fd for internal use. 703 */ 704 public FileDescriptor getFD() { 705 return fd; 706 } 707 708 private int calculateByteBufferArray(ByteBuffer[] sources, int offset, 709 int length) { 710 int sum = 0; 711 for (int val = offset; val < offset + length; val++) { 712 sum += sources[val].remaining(); 713 } 714 return sum; 715 } 716 717 /* 718 * The adapter class of DatagramSocket 719 */ 720 private static class DatagramSocketAdapter extends DatagramSocket { 721 722 /* 723 * The internal datagramChannelImpl. 724 */ 725 private DatagramChannelImpl channelImpl; 726 727 /* 728 * Constructor initialize the datagramSocketImpl and datagramChannelImpl 729 */ 730 DatagramSocketAdapter(DatagramSocketImpl socketimpl, 731 DatagramChannelImpl channelImpl) { 732 super(socketimpl); 733 this.channelImpl = channelImpl; 734 } 735 736 /* 737 * Get the internal datagramChannelImpl 738 */ 739 @Override 740 public DatagramChannel getChannel() { 741 return channelImpl; 742 } 743 744 /** 745 * @see java.net.DatagramSocket#isBound() 746 */ 747 @Override 748 public boolean isBound() { 749 return channelImpl.isBound; 750 } 751 752 /** 753 * @see java.net.DatagramSocket#isConnected() 754 */ 755 @Override 756 public boolean isConnected() { 757 return channelImpl.isConnected(); 758 } 759 760 /** 761 * @see java.net.DatagramSocket#getInetAddress() 762 */ 763 @Override 764 public InetAddress getInetAddress() { 765 if (null == channelImpl.connectAddress) { 766 return null; 767 } 768 return channelImpl.connectAddress.getAddress(); 769 } 770 771 /** 772 * @see java.net.DatagramSocket#getLocalAddress() 773 */ 774 @Override 775 public InetAddress getLocalAddress() { 776 return channelImpl.getLocalAddress(); 777 } 778 779 /** 780 * @see java.net.DatagramSocket#getPort() 781 */ 782 @Override 783 public int getPort() { 784 if (null == channelImpl.connectAddress) { 785 return -1; 786 } 787 return channelImpl.connectAddress.getPort(); 788 } 789 790 /** 791 * @see java.net.DatagramSocket#bind(java.net.SocketAddress) 792 */ 793 @Override 794 public void bind(SocketAddress localAddr) throws SocketException { 795 if (channelImpl.isConnected()) { 796 throw new AlreadyConnectedException(); 797 } 798 super.bind(localAddr); 799 channelImpl.isBound = true; 800 } 801 802 /** 803 * @see java.net.DatagramSocket#receive(java.net.DatagramPacket) 804 */ 805 @Override 806 public void receive(DatagramPacket packet) throws IOException { 807 if (!channelImpl.isBlocking()) { 808 throw new IllegalBlockingModeException(); 809 } 810 super.receive(packet); 811 } 812 813 /** 814 * @see java.net.DatagramSocket#send(java.net.DatagramPacket) 815 */ 816 @Override 817 public void send(DatagramPacket packet) throws IOException { 818 if (!channelImpl.isBlocking()) { 819 throw new IllegalBlockingModeException(); 820 } 821 super.send(packet); 822 } 823 824 /** 825 * @see java.net.DatagramSocket#close() 826 */ 827 @Override 828 public void close() { 829 synchronized (channelImpl) { 830 if (channelImpl.isOpen()) { 831 try { 832 channelImpl.close(); 833 } catch (IOException e) { 834 // Ignore 835 } 836 } 837 super.close(); 838 } 839 } 840 841 /** 842 * @see java.net.DatagramSocket#disconnect() 843 */ 844 @Override 845 public void disconnect() { 846 try { 847 channelImpl.disconnect(); 848 } catch (IOException e) { 849 // Ignore 850 } 851 super.disconnect(); 852 } 853 } 854 } 855