001: /*
002: * Copyright (c) 2000 by Matt Welsh and The Regents of the University of
003: * California. All rights reserved.
004: *
005: * Permission to use, copy, modify, and distribute this software and its
006: * documentation for any purpose, without fee, and without written agreement is
007: * hereby granted, provided that the above copyright notice and the following
008: * two paragraphs appear in all copies of this software.
009: *
010: * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
011: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
012: * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
013: * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
014: *
015: * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
017: * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
018: * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
019: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
020: *
021: * Author: Matt Welsh <mdw@cs.berkeley.edu>
022: *
023: */
024:
025: package seda.nbio;
026:
027: import java.io.*;
028: import java.net.*;
029:
030: /**
031: * A NonblockingSocket is a socket which exports nonblocking input/output
032: * streams. It is otherwise idential to a standard socket.
033: *
034: * Socket connection can be either blocking or nonblocking. Use of the
035: * standard constructors causes the current thread to block until
036: * the connection is established. Otherwise, there are 3 ways to check
037: * if the connection has been established:
038: * (1) Call <code>finishConnection</code>,
039: * (2) Call <code>connectDone</code>, or
040: * (3) Create a <code>SelectSet</code> and select on the event
041: * <code>Selectable.CONNECT_READY</code>, then call <code>connectDone</code>.
042: *
043: * @see SelectSet
044: */
045: public class NonblockingSocket extends Socket implements Selectable {
046:
047: NonblockingSocketImpl impl;
048: boolean is_connected;
049:
050: static boolean nativeLibraryLoaded = false;
051: static Object nativeLibraryLoadLock = new Object();
052:
053: static void loadNativeLibrary() {
054: synchronized (nativeLibraryLoadLock) {
055: if (!nativeLibraryLoaded) {
056: try {
057: System.loadLibrary("NBIO");
058: nativeLibraryLoaded = true;
059: } catch (Exception e) {
060: System.err
061: .println("Cannot load NBIO shared library");
062: }
063: }
064: }
065: }
066:
067: static {
068: loadNativeLibrary();
069: }
070:
071: /* Used only by NonblockingServerSocket to create an instance for accept */
072: NonblockingSocket() throws IOException {
073: impl = new NonblockingSocketImpl();
074: }
075:
076: /**
077: * Create a NonblockingSocket connection to the given host and port number.
078: * This will block until the connection is established.
079: */
080: public NonblockingSocket(String host, int port)
081: throws UnknownHostException, IOException {
082: this (InetAddress.getByName(host), port, true);
083: }
084:
085: /**
086: * Create a NonblockingSocket connection to the given host and port number.
087: * If 'block' is true, block until the connection is done.
088: */
089: public NonblockingSocket(String host, int port, boolean block)
090: throws UnknownHostException, IOException {
091: this (InetAddress.getByName(host), port, block);
092: }
093:
094: /**
095: * Create a NonblockingSocket connection to the given host and port number.
096: * This will block until the connection is established.
097: */
098: public NonblockingSocket(InetAddress address, int port)
099: throws UnknownHostException, IOException {
100: this (address, port, true);
101: }
102:
103: /**
104: * Create a NonblockingSocket connection to the given host and port number.
105: * If 'block' is true, block until the connection is done.
106: */
107: public NonblockingSocket(InetAddress address, int port,
108: boolean block) throws IOException {
109: impl = new NonblockingSocketImpl();
110:
111: if (port < 0 || port > 0xFFFF) {
112: throw new IllegalArgumentException("port out range:" + port);
113: }
114:
115: try {
116: is_connected = false;
117: impl.create(true);
118: // 'null' bind address means INADDR_ANY
119: impl.bind(null, 0);
120: impl.connect(address, port);
121: if (block)
122: finishConnect(-1);
123: } catch (IOException e) {
124: impl.close();
125: throw e;
126: }
127: }
128:
129: /**
130: * Block until the connection on this socket has been established.
131: * 'timeout' specifies the maximum number of milliseconds to block.
132: * A timeout of zero indicates no blocking (in which case this call
133: * is equivalent to <code>connectDone</code>). A timeout of -1
134: * causes this call to block indefinitely until the connection is
135: * established.
136: *
137: * @return true is the connection was established, false if still pending.
138: */
139: public boolean finishConnect(int timeout) throws SocketException {
140: if (timeout != 0) {
141: SelectSet selset = new SelectSet();
142: SelectItem selitem = new SelectItem(this ,
143: Selectable.CONNECT_READY);
144: selset.add(selitem);
145: int r = selset.select(timeout);
146: if (r == 0) {
147: return connectDone();
148: } else if (!connectDone()) {
149: // This should never happen -- connectDone() will throw an exception
150: // if there was an error
151: throw new SocketException(
152: "Socket connection not completed after select! This is a bug - please e-mail mdw@cs.berkeley.edu");
153: }
154: return true;
155: } else {
156: return connectDone();
157: }
158: }
159:
160: /**
161: * Indicate whether the connection on this socket has been established.
162: * Throws an exception if an error occurred trying to connect.
163: */
164: public boolean connectDone() throws SocketException {
165: if (is_connected)
166: return true;
167:
168: if (impl.connectDone()) {
169: is_connected = true;
170: return true;
171: } else {
172: return false;
173: }
174: }
175:
176: /**
177: * Return the remote address to which this socket is bound.
178: */
179: public InetAddress getInetAddress() {
180: return impl.getInetAddress();
181: }
182:
183: /**
184: * Return the local address to which this socket is bound.
185: */
186: public InetAddress getLocalAddress() {
187: try {
188: return InetAddress.getLocalHost();
189: } catch (Exception e) {
190: return null; // XXX MDW - Not quite right
191: }
192: }
193:
194: /**
195: * Return the remote port to which this socket is bound.
196: */
197: public int getPort() {
198: return impl.getPort();
199: }
200:
201: /**
202: * Return the local port to which this socket is bound.
203: */
204: public int getLocalPort() {
205: return impl.getLocalPort();
206: }
207:
208: /**
209: * Return an InputStream from which data on this socket can be read.
210: * The returned InputStream is actually a NonblockingInputStream
211: * and provides nonblocking semantics.
212: */
213: public InputStream getInputStream() throws IOException {
214: return impl.getInputStream();
215: }
216:
217: /**
218: * Return an OutputStream to which data on this socket can be written.
219: * The returned OutputStream is actually a NonblockingOutputStream
220: * and provides nonblocking semantics.
221: */
222: public OutputStream getOutputStream() throws IOException {
223: return impl.getOutputStream();
224: }
225:
226: /**
227: * Currently unimplemented.
228: */
229: public void setTcpNoDelay(boolean on) throws SocketException {
230: // XXX MDW Do nothing
231: }
232:
233: /**
234: * Currently unimplemented.
235: */
236: public boolean getTcpNoDelay() throws SocketException {
237: // XXX MDW Do nothing
238: return false;
239: }
240:
241: /**
242: * Currently unimplemented.
243: */
244: public void setSoLinger(boolean on, int val) throws SocketException {
245: // XXX MDW Do nothing
246: }
247:
248: /**
249: * Currently unimplemented.
250: */
251: public int getSoLinger() throws SocketException {
252: // XXX MDW Do nothing
253: return -1;
254: }
255:
256: /**
257: * Currently unimplemented.
258: */
259: public synchronized void setSoTimeout(int timeout)
260: throws SocketException {
261: // XXX MDW Do nothing
262: }
263:
264: /**
265: * Currently unimplemented.
266: */
267: public synchronized int getSoTimeout() throws SocketException {
268: // XXX MDW Do nothing
269: return 0;
270: }
271:
272: /**
273: * Closes the socket.
274: */
275: public synchronized void close() throws IOException {
276: impl.close();
277: }
278:
279: public String toString() {
280: return "NonblockingSocket[addr="
281: + impl.getInetAddress().getHostAddress() + ",port="
282: + impl.getPort() + ",localport=" + impl.getLocalPort()
283: + "]";
284: }
285:
286: }
|