/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.net; /** * ServerSocket is a TCP/IP network server socket. * <p> * Under Java and Windows CE, if no network is present, the socket constructor * may hang for an extended period of time due to the implementation of sockets * in the underlying OS. This is a known problem. * <p> * Here is an example showing the server accepting a client connection and * writing/reading some data to/from it: * * <pre> * ServerSocket server = new ServerSocket(1024); * Socket client = server.accept(); * byte[] bytes = "HELLO WORLD".getBytes(); * client.writeBytes(bytes, 0, bytes.length); * byte[] buf = new byte[10]; * int count = client.readBytes(buf, 0, buf.length); * if (count == buf.length) * ... * client.close(); * server.close(); * </pre> * * Important: you cannot open a server socket before the main event loop. In other * words, you cannot open a socket in the app's constructor, but CAN in the * initUI method. * <p> */ public class ServerSocket { Object serverRef; String addr; int port; /** * Stores the timeout value for accept operations. The value specifies the * number of milliseconds to wait before timing out an accept operation. * @see #WAIT_FOREVER */ int timeout = DEFAULT_SOTIMEOUT; /** The default value for the accept operation timeout. */ public static final int DEFAULT_SOTIMEOUT = 10000; /** The default backlog value */ public static final int DEFAULT_BACKLOG = 100; /** Passing a value of 0 to the constructor causes any accept operation to wait indefinitely. */ public static final int WAIT_FOREVER = 0; /** For internal use only */ protected ServerSocket() { } /** * Opens a server socket. This method establishes a server socket connection * at the specified port, with the default timeout for accept operations. * * @param port the local TCP port to listen for incoming connections * @throws totalcross.io.IOException */ public ServerSocket(int port) throws totalcross.io.IOException { this(port, DEFAULT_SOTIMEOUT, DEFAULT_BACKLOG, null); } /** * Opens a server socket. This method establishes a server socket connection * at the specified port, with the specified timeout for accept operations. * * @param port the local TCP port to listen for incoming connections * @param timeout the accept operation timeout * @throws totalcross.io.IOException */ public ServerSocket(int port, int timeout) throws totalcross.io.IOException { this(port, timeout, DEFAULT_BACKLOG, null); } /** * Opens a server socket. This method establishes a server socket connection * at the specified port, with the specified timeout for accept operations. * The addr argument can be used on a multi-homed host for a ServerSocket that * will only accept connect requests to one of its addresses. * * @param port the local TCP port to listen for incoming connections * @param timeout the accept operation timeout * @param addr the local address this server will bind to * @throws totalcross.io.IOException */ public ServerSocket(int port, int timeout, String addr) throws totalcross.io.IOException { this(port, timeout, DEFAULT_BACKLOG, addr); } /** * Opens a server socket. This method establishes a server socket connection * at the specified port, with the specified timeout for accept operations. * The addr argument can be used on a multi-homed host for a ServerSocket that * will only accept connect requests to one of its addresses. * * @param port the local TCP port to listen for incoming connections * @param timeout the accept operation timeout * @param backlog the limit of concurrent connections that are accepted. * @param addr the local address this server will bind to * @throws totalcross.io.IOException */ public ServerSocket(int port, int timeout, int backlog, String addr) throws totalcross.io.IOException { if (port < 0 || port > 65535) throw new java.lang.IllegalArgumentException("Invalid value for argument 'port': " + port); if (timeout < 0) throw new java.lang.IllegalArgumentException("Invalid value for argument 'timeout': " + timeout); if (backlog <= 0) throw new java.lang.IllegalArgumentException("Invalid value for argument 'backlog': " + backlog); this.port = port; this.timeout = timeout; this.addr = addr; try { serverRef = new java.net.ServerSocket(port, backlog); ((java.net.ServerSocket) serverRef).setSoTimeout(timeout); } catch (java.net.SocketException e) { throw new totalcross.io.IOException(e.getMessage()); } catch (java.io.IOException e) { throw new totalcross.io.IOException(e.getMessage()); } } /** * Returns the local address of this server socket. * @return the address to which this socket is bound, or null if the socket * is unbound. */ public String getHost() { return addr; } /** * Returns the local TCP port on which this socket is listening, passed in the constructor. * @return the port number to which this socket is listening. */ public int getLocalPort() { return port; } /** * Listens for a connection to be made to this socket and accepts it, returning * a Socket representing the connection established with the client. This * method blocks until a connection is made or the operation times out. * * @return the client Socket * @throws totalcross.io.IOException */ public Socket accept() throws totalcross.io.IOException { if (serverRef == null) throw new totalcross.io.IOException("The server socket is closed"); totalcross.net.Socket clientSocket = nativeAccept(); return clientSocket; } final private totalcross.net.Socket nativeAccept() throws totalcross.io.IOException { totalcross.net.Socket clientSocket = null; java.net.Socket socketRef; try { java.net.ServerSocket ss = (java.net.ServerSocket)serverRef; ss.setSoTimeout(timeout); if ((socketRef = ss.accept()) != null) { clientSocket = new totalcross.net.Socket(); clientSocket.socketRef = socketRef; } return clientSocket; } catch (java.net.SocketTimeoutException e) { return null; } catch (java.io.IOException e) { throw new totalcross.io.IOException(e.getMessage()); } } /** * Closes the server socket. * @throws totalcross.io.IOException */ public void close() throws totalcross.io.IOException { if (serverRef == null) throw new totalcross.io.IOException("The server socket is closed"); try { nativeClose(); } finally { serverRef = null; } } private void nativeClose() throws totalcross.io.IOException { try { ((java.net.ServerSocket) serverRef).close(); } catch (java.io.IOException e) { throw new totalcross.io.IOException(e.getMessage()); } } protected void finalize() { try { close(); } catch (totalcross.io.IOException e) { } } }