/core/com/limegroup/gnutella/io/NIOSocket.java

https://github.com/zootella/learning · Java · 1031 lines · 301 code · 193 blank · 537 comment · 23 complexity · 3314135672756005b9169be1cb6ddffa MD5 · raw file

  1. // Commented for the Learning branch
  2. package com.limegroup.gnutella.io;
  3. import java.io.IOException;
  4. import java.io.OutputStream;
  5. import java.io.InputStream;
  6. import java.nio.channels.SocketChannel;
  7. import java.nio.channels.ReadableByteChannel;
  8. import java.net.InetAddress;
  9. import java.net.Socket;
  10. import java.net.SocketTimeoutException;
  11. import java.net.UnknownHostException;
  12. import java.net.InetSocketAddress;
  13. import java.net.SocketException;
  14. import java.net.SocketAddress;
  15. import org.apache.commons.logging.LogFactory;
  16. import org.apache.commons.logging.Log;
  17. /**
  18. * A socket that does all of its connecting, reading and writing using NIO.
  19. *
  20. * This class provides input and output streams you can use for blocking IO.
  21. * The class uses non-blocking IO internally, and makes threads wait to simulate blocking.
  22. * To switch to using event-based reads, call setReadObserver().
  23. * NIO will call the handleRead() method there when it's time to read.
  24. * You have to use a ChannelReadObserver so the socket can set the appropriate underlying channel.
  25. *
  26. * NIOSocket extends Socket, meaning that it is a socket.
  27. * However, we never use that socket.
  28. * Instead, we create or get passed one and save it in the member variable named socket.
  29. * NIOSocket extends Socket just to use its interface.
  30. *
  31. * This class is for Java NIO, while socket is a java.net.Socket object.
  32. * This is because NIO didn't define a new kind of socket.
  33. * It just added NIO abilities to java.net.Socket.
  34. * With NIO, you can call Socket s; s.getChannel(); to get an NIO channel from it.
  35. * You can then read and write to that channel without blocking.
  36. * The Socket and SocketChannel objects make up a pair.
  37. *
  38. * Extends and Implements
  39. * ConnectObserver: NIO can tell this NIOSocket object a connection it initiated is made, handleConnect().
  40. * NIOMultiplexor: When NIO tells this NIOSocket object to read or write, it has objects to call to do it instead, setReadObserver() and setWriteObserver().
  41. * ReadObserver: NIO can tell this NIOSocket object to read now, handleRead().
  42. * WriteObserver: NIO can tell this NIOSocket object to get data and write now, handleWrite().
  43. */
  44. public class NIOSocket extends Socket implements ConnectObserver, NIOMultiplexor {
  45. /** Make a log for this class that we can leave notes in to document how the program runs. */
  46. private static final Log LOG = LogFactory.getLog(NIOSocket.class);
  47. /*
  48. * In NIO, a java.net.Socket and its associated java.nio.channels.SocketChannel go together as a pair.
  49. * Given one, you can ask Java for the other.
  50. * NIOSocket keeps both here, and names them socket and channel.
  51. */
  52. /** The java.net.Socket object that gets connected to the remote computer. */
  53. private final Socket socket;
  54. /** The java.nio.channels.SocketChannel object we communicate with the remote computer through. */
  55. private final SocketChannel channel;
  56. /*
  57. * NIOSocket makes 4 LimeWire objects to help exchange data with the remote computer.
  58. * For sending data, it makes a NIOOutputStream and a BufferOutputStream.
  59. * For receiving data, it makes a NIOInputStream and a BufferInputStream.
  60. *
  61. * NIOOutputStream implements the WriteObserver interface, which requires a handleWrite() method.
  62. * NIO calls handleWrite() to get the NIOOutputStream to send data from its buffer into the channel.
  63. *
  64. * NIOInputStream implements the ReadObserver interface, which requires a handleRead() method.
  65. * NIO calls handleRead() to get the NIOInputStream to read data from the channel into its buffer.
  66. *
  67. * To send data to the remote computer, call getOutputStream().
  68. * It will return a reference to the BufferOutputStream object, call write(b) on it.
  69. *
  70. * To get data the remote computer has sent us, call getInputStream().
  71. * It will return a reference to the BufferInputStream object, call read() on it.
  72. */
  73. /**
  74. * An object that implements the WriteObserver interface.
  75. * This means NIO can call handleWrite() on it when it's time for it to write.
  76. *
  77. * At first, writer will be the NIOOutputStream object the constructor made.
  78. * Then, ManagedConnection.startOutput() will call setWriteObserver, giving it a new MessageWriter instead.
  79. */
  80. private WriteObserver writer;
  81. /**
  82. * An object that implements the ReadObserver interface.
  83. * This means NIO can call handleRead() on it when it's time for it to read.
  84. *
  85. * At first, reader will be the NIOInputStream object the constructor made.
  86. * Then, ManagedConnection.loopForMessages() will call setReadObserver, giving it a new MessageReader instead.
  87. */
  88. private ReadObserver reader;
  89. /** Any exception that occurred while trying to connect */
  90. private IOException storedException = null;
  91. /**
  92. * The IP address of the remote computer our socket and channel are connected to.
  93. *
  94. * When we disconnect the channel, it won't tell us the IP address we were connected to anymore.
  95. * So, we need to keep a copy of the information here.
  96. */
  97. private InetAddress connectedTo;
  98. /** True if our connection to the remote computer has been shut down. */
  99. private boolean shuttingDown;
  100. /**
  101. * An object NIOSocket uses with wait() and notify() thread calls to make connect() block like LimeWire expects.
  102. *
  103. * When a thread calls connect(), it waits on this lock for NIO to make the connection.
  104. * When NIO makes the connection, it calls handleConnect(), which wakes the waiting thread up.
  105. *
  106. * The call to make a thread wait on the lock is LOCK.wait(timeout), this is in connect().
  107. * The call to wake that thread up is LOCK.notify(), this is in handleConnect().
  108. *
  109. * NIO is non-blocking, but this lock lets it provide a connect() method that blocks.
  110. * A large portion of LimeWire still expects the connect() call to block.
  111. */
  112. private final Object LOCK = new Object();
  113. /**
  114. * Make a new NIOSocket object for a remote computer that has connected to us.
  115. * NIOServerSocket.accept() calls this when a remote computer has connected to the listening socket in the Acceptor class.
  116. *
  117. * Saves the Socket and SocketChannel pair of objects.
  118. * Makes NIOOutputStream, BufferOutputStream, NIOInputStream, and BufferInputStream objects beneath this one.
  119. *
  120. * @param s The java.net.Socket connection socket NIO gave us for the remote computer that just connected to us
  121. */
  122. NIOSocket(Socket s) throws IOException {
  123. /*
  124. * In NIO, there are 2 objects that go together for a single connection.
  125. * java.net.Socket is the connection socket.
  126. * java.nio.channels.SocketChannel is the non-blocking communications channel.
  127. * The objects always exist as a pair.
  128. * If you have either one, you can ask Java for a reference to the other.
  129. */
  130. // Save the Socket and SocketChannel objects for this connection in this NIOSocket object
  131. channel = s.getChannel(); // Get the NIOSocketChannel object associated with the given connection java.net.Socket object
  132. socket = s;
  133. // Make NIOInputStream and NIOOutputStream objects that we'll use to exchange data with the remote computer
  134. writer = new NIOOutputStream(this, channel);
  135. reader = new NIOInputStream(this, channel);
  136. ((NIOOutputStream)writer).init(); // Have the NIOOutputStream make a BufferOutputStream and get an 8 KB ByteBuffer
  137. ((NIOInputStream)reader).init(); // Have the NIOInputStream make a BufferInputStream and get an 8 KB ByteBuffer
  138. // Tell NIO we want to know when we can send this remote computer data, and when data has arrived from it for us
  139. NIODispatcher.instance().registerReadWrite(channel, this);
  140. connectedTo = s.getInetAddress();
  141. }
  142. /**
  143. * Make a new NIOSocket object we can use later to try to connect to a remote computer.
  144. * Sockets.connectAndRelease() calls this.
  145. *
  146. * Makes a new Socket and SocketChannel pair of objects.
  147. * Makes NIOOutputStream and NIOInputStream objects beneath this one.
  148. */
  149. public NIOSocket() throws IOException {
  150. // Make a new pair of Socket and SocketChannel objects
  151. channel = SocketChannel.open(); // Call the static method java.nio.channels.SocketChannel.open() to make a new SocketChannel object
  152. socket = channel.socket(); // Get the java.net.Socket object that it goes with
  153. // Tell NIO we want to use the channel in non-blocking mode
  154. init();
  155. // Make NIOInputStream and NIOOutputStream objects that we'll use to exchange data with the remote computer
  156. writer = new NIOOutputStream(this, channel);
  157. reader = new NIOInputStream(this, channel);
  158. }
  159. /** Only unit test code uses this constructor. */
  160. public NIOSocket(InetAddress addr, int port) throws IOException {
  161. // Only unit test code uses this constructor
  162. channel = SocketChannel.open();
  163. socket = channel.socket();
  164. init();
  165. writer = new NIOOutputStream(this, channel);
  166. reader = new NIOInputStream(this, channel);
  167. connect(new InetSocketAddress(addr, port));
  168. }
  169. /** This constructor is never used. */
  170. public NIOSocket(InetAddress addr, int port, InetAddress localAddr, int localPort) throws IOException {
  171. // This constructor is never used
  172. channel = SocketChannel.open();
  173. socket = channel.socket();
  174. init();
  175. writer = new NIOOutputStream(this, channel);
  176. reader = new NIOInputStream(this, channel);
  177. bind(new InetSocketAddress(localAddr, localPort));
  178. connect(new InetSocketAddress(addr, port));
  179. }
  180. /** Only unit test code uses this constructor. */
  181. public NIOSocket(String addr, int port) throws UnknownHostException, IOException {
  182. // Only unit test code uses this constructor
  183. this(InetAddress.getByName(addr), port);
  184. }
  185. /** This constructor is never used. */
  186. public NIOSocket(String addr, int port, InetAddress localAddr, int localPort) throws IOException {
  187. // This constructor is never used
  188. this(InetAddress.getByName(addr), port, localAddr, localPort);
  189. }
  190. /** Configure our SocketChannel channel so it won't block. */
  191. private void init() throws IOException {
  192. // Turn blocking mode for the channel off
  193. channel.configureBlocking(false);
  194. }
  195. /**
  196. * Set this NIOSocket so that when NIO tells it to read, it tells the first object in the chain of readers to read instead.
  197. * The read chain will look like this:
  198. *
  199. * (this computer) - MessageReader - InflaterReader - NIOSocket - (remote computer)
  200. *
  201. * This NIOSocket object at the end contains channel, the SelectableChannel connected to the remote computer.
  202. * NIO will tell channel when there is data for channel to read.
  203. * It will do this by calling NIOSocket.handleRead().
  204. *
  205. * NIOSocket.handleRead() just calls reader.handleRead(), forwarding the call to the reader this NIOSocket object has saved.
  206. * At the start, this reader is the NIOInputStream that this NIOSocket made for itself.
  207. *
  208. * Now, MangedConnection has finished building the chain of readers.
  209. * We want NIO's command to read to hit the first object in the chain, MessageReader.
  210. * MessageReader will be told to read, it will read from InflaterReader, and InflaterReader will read from this NIOSocket object.
  211. *
  212. * This is how a push becomes a pull.
  213. * NIO's notification that the channel can read doesn't push data onto the channel.
  214. * Rather, it causes MessageReader to read, pulling data in through InflaterReader and from us.
  215. *
  216. * In ManagedConnection, loopForMessages() makes a new MessageReader, the object that slices uncompressed data into Gnutella packets.
  217. * It then calls _socket.setReadObserver(reader), giving us the MessageReader.
  218. * This is the only place this method is called.
  219. * Also, ManagedConnection calls this just one time.
  220. * As LimeWire runs, this method is only used once for each remote computer we're connected to.
  221. *
  222. * Sets the new ReadObserver.
  223. * Finds the most distant ChannelReader in the chain.
  224. * Sets its source to the prior reader, and tells it to read.
  225. * This empties any data it has buffered.
  226. * With that done, sets the source to the socket's channel.
  227. * Turns interest in reading on.
  228. *
  229. * @param newReader The MessageReader packet slicer ManagedConnection made.
  230. * The object at the start of the read chain.
  231. * The object that NIO's command to read should be forwarded to.
  232. */
  233. public void setReadObserver(final ChannelReadObserver newReader) {
  234. /*
  235. * This code defines a new class right here.
  236. * It's not named, but is Runnable, which means it has a run() method.
  237. * A thread can run a Runnable class later.
  238. * We make the class, and hand it to the NIODispatcher object's invokeLater() method.
  239. * When the right thread calls invokeLater(), it calls run() here.
  240. */
  241. // Define a new Runnable class right here, and have the NIODispatcher thread call its run() method later.
  242. NIODispatcher.instance().invokeLater(new Runnable() { public void run() {
  243. // Point oldReader and reader at this NIOSocket object's NIOInputStream
  244. ReadObserver oldReader = reader; // Point oldReader at the NIOInputStream
  245. try {
  246. // Change reader to point at the given MessageReader object instead of the NIOInputStream
  247. reader = newReader; // Point reader at the given MessageReader object
  248. ChannelReader lastChannel = newReader; // Point lastChannel at the given MessageReader object
  249. // This loop actually only runs one time
  250. while (lastChannel.getReadChannel() instanceof ChannelReader) {
  251. // The given MessageReader reads from an InflaterReader, point lastChannel at it
  252. lastChannel = (ChannelReader)lastChannel.getReadChannel(); // Point lastChannel at the InflaterReader
  253. }
  254. // This is true, oldReader is our NIOInputStream, and newReader is the given MessageReader packet slicer
  255. if (oldReader instanceof ReadableByteChannel && oldReader != newReader) {
  256. // Empty the data from our NIOInputStream, and shut it down
  257. lastChannel.setReadChannel((ReadableByteChannel)oldReader); // Set the InflaterReader to read from our NIOInputStream
  258. reader.handleRead(); // Call MessageReader.handleRead() to have it pull all the buffered data through
  259. oldReader.shutdown(); // This is the end of the NIOInputStream, call shutdown() on it
  260. }
  261. // Have the InflaterReader read directly from channel, the SocketChannel object connected to the remote computer
  262. lastChannel.setReadChannel(channel);
  263. // Register our channel with NIO so NIO will tell us when our channel has data for us to read
  264. NIODispatcher.instance().interestRead(channel, true);
  265. } catch (IOException iox) {
  266. // Shut down this NIOSocket object, and our NIOInputStream
  267. shutdown();
  268. oldReader.shutdown(); // If the exception happened before we did this above, we'll need to do it here
  269. }
  270. }});
  271. }
  272. /**
  273. * Set this NIOSocket so that when you write to it, you're actually writing to the MessageWriter at the start of the write chain.
  274. *
  275. * Only ManagedConnection.startOutput() calls this method.
  276. * startOutput() created the base of the chain of writers, which looks like this:
  277. *
  278. * MessageWriter -> DeflaterWriter -> DelayedBufferWriter -> ThrottleWriter
  279. *
  280. * Code does the following things.
  281. * Flush and remove the NIOOutputStream object we were using, but won't be a part of the chain of writers.
  282. * Find the ThrottleWriter at the end of the chain.
  283. * Make a new SocketInterestWriteAdapater.
  284. *
  285. * The SocketInterestWriteAdapter is core to how LimeWire writes.
  286. * When you call handleWrite() on this NIOSocket object, the call will go to SocketInterestWriteAdapter.handleWrite().
  287. * When ThrottleWriter writes, it sends data to the SocketInterestWriteAdapter.
  288. * The SocketInterestWriteAdapter writes to channel, the actual java.nio.channels.SocketChannel object connected to the remote computer.
  289. *
  290. * We'll use the SocketInterestWriteAdapter as a hub to receive and forward interest calls.
  291. *
  292. * @param newWriter The MessageWriter ManagedConnection made that starts the base of the chain of writers
  293. */
  294. public void setWriteObserver(final ChannelWriter newWriter) {
  295. // Define a new Runnable class right here, and have the NIODispatcher call its run() method from a different thread later.
  296. NIODispatcher.instance().invokeLater(new Runnable() { public void run() {
  297. try {
  298. /*
  299. * Before we change it, writer points at the NIOOutputStream object this NIOSocket made.
  300. * Call handleWrite() on it to make it send out all its data.
  301. * If it wasn't able to send everything out, it will return true.
  302. * This is a problem, there is still data stuck in the NIOOutputStream, and we're about to replace it with the given MessageWriter.
  303. * If this happens, throw an exception.
  304. */
  305. // Tell our NIOInputStream to send all its data to the remote computer, and make sure it has none left in it
  306. if (writer.handleWrite()) throw new IllegalStateException("data still in old writer!");
  307. writer.shutdown(); // Shut down our NIOInputStream, we're done with it
  308. // Find the ThrottleWriter at the end of the chain
  309. ChannelWriter lastChannel = newWriter; // Start lastChannel at the starting MessageWriter
  310. while (lastChannel.getWriteChannel() instanceof ChannelWriter) {
  311. // Find the object that lastChannel writes to, and point lastChannel at it instead
  312. lastChannel = (ChannelWriter)lastChannel.getWriteChannel();
  313. /*
  314. * This loop runs 3 times.
  315. * At this point, lastChannel points to: (1) DeflaterWriter, (2) DelayedBufferWriter, (3) ThrottleWriter
  316. * The last one ThrottleWriter, implements ThrottleListener, so control enters the if statement.
  317. * The loop then exits, and lastChannel points to ThrottleWriter.
  318. */
  319. // lastChannel points to ThrottleWriter
  320. if (lastChannel instanceof ThrottleListener) {
  321. // Call ThrottleListener.setAttachment(), giving it a reference to this class
  322. ((ThrottleListener)lastChannel).setAttachment(NIOSocket.this); // (ask)
  323. }
  324. }
  325. // Make a new SocketInterestWriteAdapter
  326. InterestWriteChannel source = new SocketInterestWriteAdapater(channel); // It's the object that will actually write to the Java SelectableChannel
  327. writer = source; // When you write to this NIOSocket object, you'll write to it
  328. lastChannel.setWriteChannel(source); // When the ThrottleWriter writes, it will write to it also
  329. } catch (IOException iox) {
  330. // Shut down this NIOSocket object, and our NIOOutputStream
  331. shutdown();
  332. newWriter.shutdown(); // If the exception happened before we did this above, we'll need to do it here
  333. }
  334. }});
  335. }
  336. /**
  337. * NIO calls this when our channel is connected.
  338. *
  339. * There is a thread waiting in the connect() method right now.
  340. * It told NIO to connect the channel, and then called LOCK.wait().
  341. * It's blocking in connect() until the connection is made.
  342. *
  343. * The connection is made now.
  344. * Call LOCK.notify() to wake the thread in connect() up.
  345. * It will return to its caller, having blocked for as long as it took to make the connection.
  346. */
  347. public void handleConnect() throws IOException {
  348. // Make sure only one thread can do this at a time
  349. synchronized (LOCK) {
  350. // Wake up the thread in connect() that called LOCK.wait()
  351. LOCK.notify();
  352. }
  353. }
  354. /**
  355. * NIODispatcher calls this when we can read from our channel.
  356. * Passes the call to the NIOInputStream object this NIOSocket made.
  357. * After ManagedConnection has created the chain of readers, passes the call to the MessageReader at the start of the chain.
  358. */
  359. public void handleRead() throws IOException {
  360. // Calls NIOInputStream.handleRead() or MessageReader.handleRead()
  361. reader.handleRead();
  362. }
  363. /**
  364. * NIODispatcher calls this when we can write to our channel.
  365. * Passes the call to the NIOOutputStream object this NIOSocket made.
  366. * After ManagedConnection has created the chain of writers, passes the call to the MessageWriter at the start of the chain.
  367. */
  368. public boolean handleWrite() throws IOException {
  369. // Calls NIOOutputStream.handleWrite() or MessageWriter.handleWrite()
  370. return writer.handleWrite();
  371. }
  372. /**
  373. * NIO calls this when an IOException occurred while it was processing a connect, read, or write. (do)
  374. * Shuts down the socket and all of its streams.
  375. * Wakes up any threads waiting on locks.
  376. *
  377. * @param iox The IOException that happened
  378. */
  379. public void handleIOException(IOException iox) {
  380. // Make sure only one thread can do this at a time
  381. synchronized (LOCK) {
  382. /*
  383. * Save the given exception in storedException.
  384. * Call LOCK.notify() to wake up the thread sleeping in the connect() method.
  385. * It will check out storedException.
  386. * If there is an exception there, it will call shtudown(), and then throw it.
  387. */
  388. // Save the given exception in storedException, and wake up the thread in connect() so it will get it
  389. storedException = iox;
  390. LOCK.notify();
  391. }
  392. // Shut this NIOSocket object down
  393. shutdown(); // The shuttingDown boolean will keep the method from running twice
  394. }
  395. /**
  396. * Shut down this socket and all of its streams.
  397. * Wake up threads waiting on locks.
  398. *
  399. * Closing things and shutting them down can cause IOExceptions.
  400. * Code here catches them, and does nothing, ignoring them.
  401. * There's nothing we can do about internal errors.
  402. * And, we're shutting down anyway, so it doesn't matter if something breaks.
  403. *
  404. * The Shutdownable interface requires this method.
  405. */
  406. public void shutdown() {
  407. // Only let one thread in this section at a time
  408. synchronized (LOCK) {
  409. // Make sure this shutdown() method only runs once
  410. if (shuttingDown) return; // If it's already run, leave now
  411. shuttingDown = true; // Mark shuttingDown true so we won't get past here again
  412. }
  413. // If the Apache log is on, make a note that we're shutting down
  414. if (LOG.isDebugEnabled()) LOG.debug("Shutting down socket & streams for: " + this);
  415. /*
  416. * socket is a java.net.Socket object that we're using with NIO.
  417. * The method calls below call socket.shutdownInput() and socket.shutdownOutput().
  418. * This tells Java we won't be reading or writing from the socket anymore.
  419. * It also ends our ability to do these things.
  420. */
  421. // Shut down this object's socket for reading and writing
  422. try { shutdownInput(); } catch (IOException ignored) {} // Calls socket.shutdownInput();
  423. try { shutdownOutput(); } catch (IOException ignored) {} // Calls socket.shutdownOutput();
  424. // Tell our NIOInputStream and NIOOutputStream objects to shut down
  425. reader.shutdown();
  426. writer.shutdown();
  427. try {
  428. // Close the java.net.Socket object
  429. socket.close();
  430. // Ignore exceptions
  431. } catch (IOException ignored) {
  432. } catch (Error ignored) {
  433. }
  434. // Close the NIO SocketChannel we got from our socket
  435. try { channel.close(); } catch (IOException ignored) {}
  436. // Only let one thread in this section at a time
  437. synchronized (LOCK) {
  438. /*
  439. * Another thread may have called LOCK.wait();
  440. * That made it block right there.
  441. * When we call LOCK.notify(), that thread will keep going.
  442. */
  443. // Wake up a thread waiting on the lock object
  444. LOCK.notify();
  445. }
  446. }
  447. /** Not used. */
  448. public void bind(SocketAddress endpoint) throws IOException {
  449. // Have the socket do this
  450. socket.bind(endpoint);
  451. }
  452. /**
  453. * Shut down this socket and all of its streams.
  454. * Wake up threads waiting on locks.
  455. */
  456. public void close() throws IOException {
  457. // This just calls the shutdown() method here in NIOSocket
  458. NIODispatcher.instance().shutdown(this);
  459. }
  460. /** Only used in test code. */
  461. public void connect(SocketAddress addr) throws IOException {
  462. // Connect with no timeout, wait forever if necessary
  463. connect(addr, 0);
  464. }
  465. /**
  466. * Have NIO connect the SocketChannel to the given address, and wait here until it makes the connection.
  467. *
  468. * This method uses Java NIO to connect to the remote computer.
  469. * NIO doesn't block, the call channel.connect(addr) below returns immediately, and you can find out later if it worked.
  470. * But, the part of LimeWire above this method is built to have a thread wait to connect and then wait as we exchange handshake headers.
  471. * So, this method fakes blocking for the benefit of the rest of the program.
  472. * It takes a new, robust non-blocking NIO system and converts it to an old-fashioned, blocking one.
  473. *
  474. * A thread is meant to call connect(), get stuck here, and leave once we're connected or a timeout has expired.
  475. * Later, when we start exchanging packets with the remote computer through this connection, we'll use NIO without this fake blocking.
  476. *
  477. * @param addr The IP address and port number to try to connect to
  478. * @param timeout After telling NIO to connect, the thread will wait here for up to 6 seconds
  479. */
  480. public void connect(SocketAddress addr, int timeout) throws IOException {
  481. /*
  482. * Here are 3 Java types that hold IP address and port number information.
  483. * SocketAddress and InetSocketAddress are the same.
  484. * They hold the IP address and port number together.
  485. * InetAddress just holds the IP address.
  486. */
  487. // Keep a record of the IP address and port number we connected to
  488. connectedTo = ((InetSocketAddress)addr).getAddress();
  489. // Make sur the given InetSocketAddress contains an IP address
  490. InetSocketAddress iaddr = (InetSocketAddress)addr;
  491. if (iaddr.isUnresolved()) throw new IOException("unresolved: " + addr);
  492. // Only let one thread in this section at a time
  493. synchronized (LOCK) {
  494. /*
  495. * This is where LimeWire actually asks Java to connect to an IP address and port number.
  496. *
  497. * Here's how to connect to a remote computer the NIO way.
  498. * Make a Socket, and then call getChannel() on it to get its associated SocketChannel object.
  499. * Here, it's named channel.
  500. * Package the IP address and port number you want to connect to in an InetSocketAddress object.
  501. * Here, it's named addr.
  502. * Call channel.connect(addr).
  503. *
  504. * If Java can establish the connection immediately, the connect(addr) method returns true.
  505. * This usually happens on local connections.
  506. * If it's going to take a little while, connect(addr) returns false.
  507. *
  508. * This call doesn't block, and returns right away.
  509. * NIO will tell you later if it was able to make the connection.
  510. */
  511. // Have Java try to open a connection to the given IP address and port number
  512. if (!channel.connect(addr)) {
  513. /*
  514. * channel.connect(addr) returned false.
  515. * This means it's going to take a little while for NIO to contact the remote computer.
  516. */
  517. // Tell NIO that we are interested in knowing when this channel has been connected
  518. NIODispatcher.instance().registerConnect(
  519. channel, // The channel that NIO is connecting to the remote computer
  520. this); // NIO will call this object's handleConnect() method when the connection is made
  521. try {
  522. /*
  523. * Have this thread wait here for up to 6 seconds.
  524. * When NIO calls handleConnect() above, the thread running that method will call LOCK.notify().
  525. * When it does that, it wakes up the thread here.
  526. */
  527. // Wait here until NIO calls handleConnect(), meaning the connection is made
  528. LOCK.wait(timeout); // Or 6 seconds pass
  529. // If another thread called LOCK.interrupt(), the wait() method above will throw an InterruptedException
  530. } catch (InterruptedException ix) {
  531. // Wrap it in an InterruptedIOException and throw it upward
  532. throw new InterruptedIOException(ix);
  533. }
  534. /*
  535. * NIO may have called the handleIOException() method above.
  536. * If it did, that method caught the exception it threw us and stored it in storedException.
  537. */
  538. // Move the exception from storedException to x
  539. IOException x = storedException;
  540. storedException = null;
  541. // If NIO did throw us an exception
  542. if (x != null) {
  543. // Shut down this NIOSocket object and all of the objects it made to use, and throw the exception upwards
  544. shutdown();
  545. throw x;
  546. }
  547. // If the socket isn't connected
  548. if (!isConnected()) { // wait above must have timed out after 6 seconds instead of getting woken up by handleConnect()
  549. // Shut down this NIOSocket object and all of the objects it made to use, and throw an exception that explains how long we waited
  550. shutdown();
  551. throw new SocketTimeoutException("couldn't connect in " + timeout + " milliseconds");
  552. }
  553. }
  554. /*
  555. * If channel.connect(addr) makes the connection immediately, it returns true.
  556. * Control reaches here without running the inside of the if block at all.
  557. */
  558. }
  559. // Make a note about the connection we just made in the Apache debugging log
  560. if (LOG.isTraceEnabled()) LOG.trace("Connected to: " + addr);
  561. // This NIOSocket object made NIOOutputStream and NIOInputStream objects, call init() on both of them
  562. if (writer instanceof NIOOutputStream) ((NIOOutputStream)writer).init(); // Have NIOOutputStream make a BufferOutputStream and get an 8 KB buffer
  563. if (reader instanceof NIOInputStream) ((NIOInputStream)reader).init(); // Have NIOInputStream make a BufferInputStream and get an 8 KB buffer
  564. }
  565. /**
  566. * The IP address of the remote computer this socket is connected to.
  567. *
  568. * If the remote computer connected to us, we got the IP address from socket.getInetAddress().
  569. * If we connected to the remote computer, we saved the given IP address before we tried to connect to it.
  570. */
  571. public InetAddress getInetAddress() {
  572. // Return the value we saved
  573. return connectedTo;
  574. }
  575. /**
  576. * Get the object this NIOSocket made that implements InputStream and you can read from.
  577. * Calls to read will block, even though the NIO underneath is non-blocking.
  578. *
  579. * @return The BufferInputStream the NIOInputSteam made, which you can call read() on
  580. */
  581. public InputStream getInputStream() throws IOException {
  582. // Make sure the socket is open
  583. if (isClosed()) throw new IOException("Socket closed.");
  584. // Return a reference to the BufferInputStream the NIOInputStream made
  585. if (reader instanceof NIOInputStream) return ((NIOInputStream)reader).getInputStream();
  586. else throw new IllegalStateException("reader not NIOInputStream!");
  587. }
  588. /**
  589. * Get the object this NIOSocket made that implements OutputStream and you can write to.
  590. * Calls to write will block, even though the NIO underneath is non-blocking.
  591. *
  592. * @return The BufferOutputStream the NIOOutputStream made, which you can call write(b) on
  593. */
  594. public OutputStream getOutputStream() throws IOException {
  595. // Make sure the socket is open
  596. if (isClosed()) throw new IOException("Socket closed.");
  597. // Return a reference to the BufferOutputStream the NIOOutputStream made
  598. if (writer instanceof NIOOutputStream) return ((NIOOutputStream)writer).getOutputStream();
  599. else throw new IllegalStateException("writer not NIOOutputStream!");
  600. }
  601. /*
  602. * LimeWire's NIOSocket class extends Socket, so it is a socket.
  603. * It also has a member object of type Socket named socket, so it contains a socket.
  604. * So there are 2 different sockets here.
  605. *
  606. * NIOSocket never uses the socket that it is, and only uses the socket that it contains instead.
  607. * This class extends Socket just to steal its interface.
  608. * It overrides methods Socket defines and passes them on to the member socket object.
  609. */
  610. /**
  611. * Return the NIO SocketChannel object associated with the Socket this NIOSocket object keeps.
  612. */
  613. public SocketChannel getChannel() {
  614. // Have the socket do this
  615. return socket.getChannel();
  616. }
  617. /**
  618. * Only test code calls this.
  619. * Get the local port number the Socket this NIOSocket object keeps is bound to.
  620. */
  621. public int getLocalPort() {
  622. // Have the socket do this
  623. return socket.getLocalPort();
  624. }
  625. /** Not used. */
  626. public SocketAddress getLocalSocketAddress() {
  627. // Have the socket do this
  628. return socket.getLocalSocketAddress();
  629. }
  630. /**
  631. * Use the connection socket in this NIOSocket object to find out what our IP address is.
  632. * Asks the socket for the local address, the one on the near end.
  633. * This is still just our LAN address, but it's a little better than calling InetAddress.getLocalHost().
  634. *
  635. * @return Our IP address as an InetAddress object
  636. */
  637. public InetAddress getLocalAddress() {
  638. try {
  639. // Have the socket do this
  640. return socket.getLocalAddress();
  641. } catch (Error osxSucks) {
  642. /*
  643. * On Macintosh OS X 10.3 with Java 1.4.2_05,
  644. * if the connection dies and you call socket.getLocalAddress(),
  645. * it will throw an exception.
  646. */
  647. try {
  648. // Ask Java what our IP address is, and return that instead
  649. return InetAddress.getLocalHost(); // Returns our LAN IP address, like 192.168.1.102
  650. // InetAddress.getLocalHost can sometimes throw an exception, catch it and return null instead
  651. } catch (UnknownHostException uhe) { return null; }
  652. }
  653. }
  654. /** Not used. */
  655. public boolean getOOBInline() throws SocketException {
  656. // Have the socket do this
  657. return socket.getOOBInline();
  658. }
  659. /**
  660. * The remote computer's port number.
  661. * Calls socket.getPort() to ask the connection socket what port number on on its far end.
  662. *
  663. * @return The port number the socket is connected to
  664. */
  665. public int getPort() {
  666. // Ask the socket this
  667. return socket.getPort();
  668. }
  669. /** Not used. */
  670. public int getReceiveBufferSize() throws SocketException {
  671. // Ask the socket this
  672. return socket.getReceiveBufferSize();
  673. }
  674. /** Not used. */
  675. public boolean getReuseAddress() throws SocketException {
  676. // Ask the socket this
  677. return socket.getReuseAddress();
  678. }
  679. /** Not used. */
  680. public int getSendBufferSize() throws SocketException {
  681. // Ask the socket this
  682. return socket.getSendBufferSize();
  683. }
  684. /** Not used. */
  685. public int getSoLinger() throws SocketException {
  686. // Ask the socket this
  687. return socket.getSoLinger();
  688. }
  689. /**
  690. * Find out how long the socket will wait for something to happen before giving up.
  691. *
  692. * @param The socket timeout, or 0 if the socket will wait forever
  693. */
  694. public int getSoTimeout() throws SocketException {
  695. // Ask the socket this
  696. return socket.getSoTimeout();
  697. }
  698. /** Not used. */
  699. public boolean getTcpNoDelay() throws SocketException {
  700. // Ask the socket this
  701. return socket.getTcpNoDelay();
  702. }
  703. /** Not used. */
  704. public int getTrafficClass() throws SocketException {
  705. // Ask the socket this
  706. return socket.getTrafficClass();
  707. }
  708. /** Not used. */
  709. public boolean isBound() {
  710. // Ask the socket this
  711. return socket.isBound();
  712. }
  713. /**
  714. * Ask the connection socket in this NIOSocket object if it's closed or not.
  715. *
  716. * @return True if the socket is closed
  717. */
  718. public boolean isClosed() {
  719. // Ask the socket this
  720. return socket.isClosed();
  721. }
  722. /**
  723. * Ask the connection socket in this NIOSocket object if it's connected to a remote computer or not.
  724. *
  725. * @return True if the socket is connected.
  726. */
  727. public boolean isConnected() {
  728. // Ask the socket this
  729. return socket.isConnected();
  730. }
  731. /** Not used. */
  732. public boolean isInputShutdown() {
  733. // Ask the socket this
  734. return socket.isInputShutdown();
  735. }
  736. /** Not used. */
  737. public boolean isOutputShutdown() {
  738. // Ask the socket this
  739. return socket.isOutputShutdown();
  740. }
  741. /** Not used. */
  742. public void sendUrgentData(int data) {
  743. // Instead of having the socket do this, throw an exception
  744. throw new UnsupportedOperationException("No urgent data.");
  745. }
  746. /**
  747. * Enable or disable SO_KEEPALIVE on the connection socket this NIOSocket object keeps.
  748. *
  749. * @param b True to enable the SO_KEEPALIVE socket option, false to disable it
  750. */
  751. public void setKeepAlive(boolean b) throws SocketException {
  752. // Have the socket do this
  753. socket.setKeepAlive(b);
  754. }
  755. /** Not used. */
  756. public void setOOBInline(boolean b) throws SocketException {
  757. // Have the socket do this
  758. socket.setOOBInline(b);
  759. }
  760. /** Not used. */
  761. public void setReceiveBufferSize(int size) throws SocketException {
  762. // Have the socket do this
  763. socket.setReceiveBufferSize(size);
  764. }
  765. /** Not used. */
  766. public void setReuseAddress(boolean on) throws SocketException {
  767. // Have the socket do this
  768. socket.setReuseAddress(on);
  769. }
  770. /** Not used. */
  771. public void setSendBufferSize(int size) throws SocketException {
  772. // Set the socket this way
  773. socket.setSendBufferSize(size);
  774. }
  775. /** Not used. */
  776. public void setSoLinger(boolean on, int linger) throws SocketException {
  777. // Set the socket this way
  778. socket.setSoLinger(on, linger);
  779. }
  780. /**
  781. * Set the amount of time the socket in this NIOSocket object should wait before giving up.
  782. *
  783. * @param timeout The timeout, in milliseconds
  784. */
  785. public void setSoTimeout(int timeout) throws SocketException {
  786. // Set the socket this way
  787. socket.setSoTimeout(timeout);
  788. }
  789. /** Not used. */
  790. public void setTcpNoDelay(boolean b) throws SocketException {
  791. // Set the socket this way
  792. socket.setTcpNoDelay(b);
  793. }
  794. /** Not used. */
  795. public void setTrafficClass(int i) throws SocketException {
  796. // Set the socket this way
  797. socket.setTrafficClass(i);
  798. }
  799. /**
  800. * Shut down the socket so we can't read from it anymore.
  801. *
  802. * socket is a java.net.Socket object that we're using with NIO.
  803. * This method calls the shutdownInput() method on our Socket object.
  804. *
  805. * Places the input stream for this socket at the end of the stream.
  806. * Any data sent to the input stream side of the socket is acknowledged and then silently discarded.
  807. * If you read from a socket input stream after invoking shutdownInput() on the socket, the stream will return EOF.
  808. */
  809. public void shutdownInput() throws IOException {
  810. // Shut down the socket for reading
  811. socket.shutdownInput();
  812. }
  813. /**
  814. * Shut down the socket so we can't write to it anymore.
  815. *
  816. * socket is a java.net.Socket object that we're using with NIO.
  817. * This method calls the shutdownOutput() method on our Socket object.
  818. *
  819. * Disables the output stream for this socket.
  820. *
  821. * This is a TCP socket.
  822. * Java will actually send the data we've previously written to the socket, and then do the TCP connection termination sequence.
  823. * If you write to a socket output stream after invoking shutdownOutput() on the socket, the stream will throw an IOException.
  824. */
  825. public void shutdownOutput() throws IOException {
  826. // Shut down the socket for writing
  827. socket.shutdownOutput();
  828. }
  829. /**
  830. * Get information about this NIOSocket object as text in a string.
  831. *
  832. * @return Text like (do)
  833. */
  834. public String toString() {
  835. // Compose text like (do)
  836. return "NIOSocket::" + channel.toString();
  837. }
  838. }