001: /*
002: * Copyright 2001-2005 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.apache.commons.net;
017:
018: import java.io.IOException;
019: import java.io.InputStream;
020: import java.io.OutputStream;
021: import java.net.InetAddress;
022: import java.net.Socket;
023: import java.net.SocketException;
024:
025: /**
026: * The SocketClient provides the basic operations that are required of
027: * client objects accessing sockets. It is meant to be
028: * subclassed to avoid having to rewrite the same code over and over again
029: * to open a socket, close a socket, set timeouts, etc. Of special note
030: * is the {@link #setSocketFactory setSocketFactory }
031: * method, which allows you to control the type of Socket the SocketClient
032: * creates for initiating network connections. This is especially useful
033: * for adding SSL or proxy support as well as better support for applets. For
034: * example, you could create a
035: * {@link org.apache.commons.net.SocketFactory} that
036: * requests browser security capabilities before creating a socket.
037: * All classes derived from SocketClient should use the
038: * {@link #_socketFactory_ _socketFactory_ } member variable to
039: * create Socket and ServerSocket instances rather than instanting
040: * them by directly invoking a constructor. By honoring this contract
041: * you guarantee that a user will always be able to provide his own
042: * Socket implementations by substituting his own SocketFactory.
043: * @author Daniel F. Savarese
044: * @see SocketFactory
045: */
046: public abstract class SocketClient {
047: /**
048: * The end of line character sequence used by most IETF protocols. That
049: * is a carriage return followed by a newline: "\r\n"
050: */
051: public static final String NETASCII_EOL = "\r\n";
052:
053: /** The default SocketFactory shared by all SocketClient instances. */
054: private static final SocketFactory __DEFAULT_SOCKET_FACTORY = new DefaultSocketFactory();
055:
056: /** The timeout to use after opening a socket. */
057: protected int _timeout_;
058:
059: /** The socket used for the connection. */
060: protected Socket _socket_;
061:
062: /**
063: * A status variable indicating if the client's socket is currently open.
064: */
065: protected boolean _isConnected_;
066:
067: /** The default port the client should connect to. */
068: protected int _defaultPort_;
069:
070: /** The socket's InputStream. */
071: protected InputStream _input_;
072:
073: /** The socket's OutputStream. */
074: protected OutputStream _output_;
075:
076: /** The socket's SocketFactory. */
077: protected SocketFactory _socketFactory_;
078:
079: /**
080: * Default constructor for SocketClient. Initializes
081: * _socket_ to null, _timeout_ to 0, _defaultPort to 0,
082: * _isConnected_ to false, and _socketFactory_ to a shared instance of
083: * {@link org.apache.commons.net.DefaultSocketFactory}.
084: */
085: public SocketClient() {
086: _socket_ = null;
087: _input_ = null;
088: _output_ = null;
089: _timeout_ = 0;
090: _defaultPort_ = 0;
091: _isConnected_ = false;
092: _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
093: }
094:
095: /**
096: * Because there are so many connect() methods, the _connectAction_()
097: * method is provided as a means of performing some action immediately
098: * after establishing a connection, rather than reimplementing all
099: * of the connect() methods. The last action performed by every
100: * connect() method after opening a socket is to call this method.
101: * <p>
102: * This method sets the timeout on the just opened socket to the default
103: * timeout set by {@link #setDefaultTimeout setDefaultTimeout() },
104: * sets _input_ and _output_ to the socket's InputStream and OutputStream
105: * respectively, and sets _isConnected_ to true.
106: * <p>
107: * Subclasses overriding this method should start by calling
108: * <code> super._connectAction_() </code> first to ensure the
109: * initialization of the aforementioned protected variables.
110: */
111: protected void _connectAction_() throws IOException {
112: _socket_.setSoTimeout(_timeout_);
113: _input_ = _socket_.getInputStream();
114: _output_ = _socket_.getOutputStream();
115: _isConnected_ = true;
116: }
117:
118: /**
119: * Opens a Socket connected to a remote host at the specified port and
120: * originating from the current host at a system assigned port.
121: * Before returning, {@link #_connectAction_ _connectAction_() }
122: * is called to perform connection initialization actions.
123: * <p>
124: * @param host The remote host.
125: * @param port The port to connect to on the remote host.
126: * @exception SocketException If the socket timeout could not be set.
127: * @exception IOException If the socket could not be opened. In most
128: * cases you will only want to catch IOException since SocketException is
129: * derived from it.
130: */
131: public void connect(InetAddress host, int port)
132: throws SocketException, IOException {
133: _socket_ = _socketFactory_.createSocket(host, port);
134: _connectAction_();
135: }
136:
137: /**
138: * Opens a Socket connected to a remote host at the specified port and
139: * originating from the current host at a system assigned port.
140: * Before returning, {@link #_connectAction_ _connectAction_() }
141: * is called to perform connection initialization actions.
142: * <p>
143: * @param hostname The name of the remote host.
144: * @param port The port to connect to on the remote host.
145: * @exception SocketException If the socket timeout could not be set.
146: * @exception IOException If the socket could not be opened. In most
147: * cases you will only want to catch IOException since SocketException is
148: * derived from it.
149: * @exception UnknownHostException If the hostname cannot be resolved.
150: */
151: public void connect(String hostname, int port)
152: throws SocketException, IOException {
153: _socket_ = _socketFactory_.createSocket(hostname, port);
154: _connectAction_();
155: }
156:
157: /**
158: * Opens a Socket connected to a remote host at the specified port and
159: * originating from the specified local address and port.
160: * Before returning, {@link #_connectAction_ _connectAction_() }
161: * is called to perform connection initialization actions.
162: * <p>
163: * @param host The remote host.
164: * @param port The port to connect to on the remote host.
165: * @param localAddr The local address to use.
166: * @param localPort The local port to use.
167: * @exception SocketException If the socket timeout could not be set.
168: * @exception IOException If the socket could not be opened. In most
169: * cases you will only want to catch IOException since SocketException is
170: * derived from it.
171: */
172: public void connect(InetAddress host, int port,
173: InetAddress localAddr, int localPort)
174: throws SocketException, IOException {
175: _socket_ = _socketFactory_.createSocket(host, port, localAddr,
176: localPort);
177: _connectAction_();
178: }
179:
180: /**
181: * Opens a Socket connected to a remote host at the specified port and
182: * originating from the specified local address and port.
183: * Before returning, {@link #_connectAction_ _connectAction_() }
184: * is called to perform connection initialization actions.
185: * <p>
186: * @param hostname The name of the remote host.
187: * @param port The port to connect to on the remote host.
188: * @param localAddr The local address to use.
189: * @param localPort The local port to use.
190: * @exception SocketException If the socket timeout could not be set.
191: * @exception IOException If the socket could not be opened. In most
192: * cases you will only want to catch IOException since SocketException is
193: * derived from it.
194: * @exception UnknownHostException If the hostname cannot be resolved.
195: */
196: public void connect(String hostname, int port,
197: InetAddress localAddr, int localPort)
198: throws SocketException, IOException {
199: _socket_ = _socketFactory_.createSocket(hostname, port,
200: localAddr, localPort);
201: _connectAction_();
202: }
203:
204: /**
205: * Opens a Socket connected to a remote host at the current default port
206: * and originating from the current host at a system assigned port.
207: * Before returning, {@link #_connectAction_ _connectAction_() }
208: * is called to perform connection initialization actions.
209: * <p>
210: * @param host The remote host.
211: * @exception SocketException If the socket timeout could not be set.
212: * @exception IOException If the socket could not be opened. In most
213: * cases you will only want to catch IOException since SocketException is
214: * derived from it.
215: */
216: public void connect(InetAddress host) throws SocketException,
217: IOException {
218: connect(host, _defaultPort_);
219: }
220:
221: /**
222: * Opens a Socket connected to a remote host at the current default
223: * port and originating from the current host at a system assigned port.
224: * Before returning, {@link #_connectAction_ _connectAction_() }
225: * is called to perform connection initialization actions.
226: * <p>
227: * @param hostname The name of the remote host.
228: * @exception SocketException If the socket timeout could not be set.
229: * @exception IOException If the socket could not be opened. In most
230: * cases you will only want to catch IOException since SocketException is
231: * derived from it.
232: * @exception UnknownHostException If the hostname cannot be resolved.
233: */
234: public void connect(String hostname) throws SocketException,
235: IOException {
236: connect(hostname, _defaultPort_);
237: }
238:
239: /**
240: * Disconnects the socket connection.
241: * You should call this method after you've finished using the class
242: * instance and also before you call
243: * {@link #connect connect() }
244: * again. _isConnected_ is set to false, _socket_ is set to null,
245: * _input_ is set to null, and _output_ is set to null.
246: * <p>
247: * @exception IOException If there is an error closing the socket.
248: */
249: public void disconnect() throws IOException {
250: _socket_.close();
251: _input_.close();
252: _output_.close();
253: _socket_ = null;
254: _input_ = null;
255: _output_ = null;
256: _isConnected_ = false;
257: }
258:
259: /**
260: * Returns true if the client is currently connected to a server.
261: * <p>
262: * @return True if the client is currently connected to a server,
263: * false otherwise.
264: */
265: public boolean isConnected() {
266: return _isConnected_;
267: }
268:
269: /**
270: * Sets the default port the SocketClient should connect to when a port
271: * is not specified. The {@link #_defaultPort_ _defaultPort_ }
272: * variable stores this value. If never set, the default port is equal
273: * to zero.
274: * <p>
275: * @param port The default port to set.
276: */
277: public void setDefaultPort(int port) {
278: _defaultPort_ = port;
279: }
280:
281: /**
282: * Returns the current value of the default port (stored in
283: * {@link #_defaultPort_ _defaultPort_ }).
284: * <p>
285: * @return The current value of the default port.
286: */
287: public int getDefaultPort() {
288: return _defaultPort_;
289: }
290:
291: /**
292: * Set the default timeout in milliseconds to use when opening a socket.
293: * This value is only used previous to a call to
294: * {@link #connect connect()}
295: * and should not be confused with {@link #setSoTimeout setSoTimeout()}
296: * which operates on an the currently opened socket. _timeout_ contains
297: * the new timeout value.
298: * <p>
299: * @param timeout The timeout in milliseconds to use for the socket
300: * connection.
301: */
302: public void setDefaultTimeout(int timeout) {
303: _timeout_ = timeout;
304: }
305:
306: /**
307: * Returns the default timeout in milliseconds that is used when
308: * opening a socket.
309: * <p>
310: * @return The default timeout in milliseconds that is used when
311: * opening a socket.
312: */
313: public int getDefaultTimeout() {
314: return _timeout_;
315: }
316:
317: /**
318: * Set the timeout in milliseconds of a currently open connection.
319: * Only call this method after a connection has been opened
320: * by {@link #connect connect()}.
321: * <p>
322: * @param timeout The timeout in milliseconds to use for the currently
323: * open socket connection.
324: * @exception SocketException If the operation fails.
325: */
326: public void setSoTimeout(int timeout) throws SocketException {
327: _socket_.setSoTimeout(timeout);
328: }
329:
330: /**
331: * Returns the timeout in milliseconds of the currently opened socket.
332: * <p>
333: * @return The timeout in milliseconds of the currently opened socket.
334: * @exception SocketException If the operation fails.
335: */
336: public int getSoTimeout() throws SocketException {
337: return _socket_.getSoTimeout();
338: }
339:
340: /**
341: * Enables or disables the Nagle's algorithm (TCP_NODELAY) on the
342: * currently opened socket.
343: * <p>
344: * @param on True if Nagle's algorithm is to be enabled, false if not.
345: * @exception SocketException If the operation fails.
346: */
347: public void setTcpNoDelay(boolean on) throws SocketException {
348: _socket_.setTcpNoDelay(on);
349: }
350:
351: /**
352: * Returns true if Nagle's algorithm is enabled on the currently opened
353: * socket.
354: * <p>
355: * @return True if Nagle's algorithm is enabled on the currently opened
356: * socket, false otherwise.
357: * @exception SocketException If the operation fails.
358: */
359: public boolean getTcpNoDelay() throws SocketException {
360: return _socket_.getTcpNoDelay();
361: }
362:
363: /**
364: * Sets the SO_LINGER timeout on the currently opened socket.
365: * <p>
366: * @param on True if linger is to be enabled, false if not.
367: * @param val The linger timeout (in hundredths of a second?)
368: * @exception SocketException If the operation fails.
369: */
370: public void setSoLinger(boolean on, int val) throws SocketException {
371: _socket_.setSoLinger(on, val);
372: }
373:
374: /**
375: * Returns the current SO_LINGER timeout of the currently opened socket.
376: * <p>
377: * @return The current SO_LINGER timeout. If SO_LINGER is disabled returns
378: * -1.
379: * @exception SocketException If the operation fails.
380: */
381: public int getSoLinger() throws SocketException {
382: return _socket_.getSoLinger();
383: }
384:
385: /**
386: * Returns the port number of the open socket on the local host used
387: * for the connection.
388: * <p>
389: * @return The port number of the open socket on the local host used
390: * for the connection.
391: */
392: public int getLocalPort() {
393: return _socket_.getLocalPort();
394: }
395:
396: /**
397: * Returns the local address to which the client's socket is bound.
398: * <p>
399: * @return The local address to which the client's socket is bound.
400: */
401: public InetAddress getLocalAddress() {
402: return _socket_.getLocalAddress();
403: }
404:
405: /**
406: * Returns the port number of the remote host to which the client is
407: * connected.
408: * <p>
409: * @return The port number of the remote host to which the client is
410: * connected.
411: */
412: public int getRemotePort() {
413: return _socket_.getPort();
414: }
415:
416: /**
417: * @return The remote address to which the client is connected.
418: */
419: public InetAddress getRemoteAddress() {
420: return _socket_.getInetAddress();
421: }
422:
423: /**
424: * Verifies that the remote end of the given socket is connected to the
425: * the same host that the SocketClient is currently connected to. This
426: * is useful for doing a quick security check when a client needs to
427: * accept a connection from a server, such as an FTP data connection or
428: * a BSD R command standard error stream.
429: * <p>
430: * @return True if the remote hosts are the same, false if not.
431: */
432: public boolean verifyRemote(Socket socket) {
433: InetAddress host1, host2;
434:
435: host1 = socket.getInetAddress();
436: host2 = getRemoteAddress();
437:
438: return host1.equals(host2);
439: }
440:
441: /**
442: * Sets the SocketFactory used by the SocketClient to open socket
443: * connections. If the factory value is null, then a default
444: * factory is used (only do this to reset the factory after having
445: * previously altered it).
446: * <p>
447: * @param factory The new SocketFactory the SocketClient should use.
448: */
449: public void setSocketFactory(SocketFactory factory) {
450: if (factory == null)
451: _socketFactory_ = __DEFAULT_SOCKET_FACTORY;
452: else
453: _socketFactory_ = factory;
454: }
455: }
|