// $Id$ // // Copyright 2007-2008 Cisco Systems Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy // of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; namespace Etch.Util { /// /// Implementation of a connection which handles a socket listener. /// public class TcpListener : Connection, Transport { /// /// Constructs the TcpListener. /// /// max number of pending connection requests. /// address to listen to. Null means any local address. /// port to listen on. Port must be >= 0. Port of 0 /// means allocate an available port. /// how long in milliseconds to wait before retrying a /// failure. Delay must be >= 0. Delay of 0 means do not retry. private TcpListener( int backlog, String host, int port, int delay ) { if ( backlog < 0 ) throw new ArgumentException( "backlog < 0" ); if ( port < 0 || port > 65535 ) throw new ArgumentException( "port < 0 || port > 65535" ); if ( delay < 0 ) throw new ArgumentException( "delay < 0" ); this.backlog = backlog; this.host = host; this.port = port; this.delay = delay; } /// /// Query term from URI to specify backlog value to ServerSocket. The /// value is "TcpListener.backlog". /// public const String BACKLOG = "TcpListener.backlog"; public TcpListener(URL uri, Resources resources) : this((int)uri.GetIntegerTerm(BACKLOG, 0), TranslateHost(uri.Host), uri.Port != null ? uri.Port.Value : 0, 0) { // nothing else. } /// /// Constructs a TcpListener, initialized from the URI. Listens on the host /// and port specified in the URI. To listen on all interfaces, specify /// host as ALL_INTFS ("0.0.0.0"). If port is specified or defaulted to 0, /// an unused port will be selected. /// /// public TcpListener(string uri, Resources resources) : this(new URL(uri), resources) { // nothing else. } private int backlog; private String host; private int port; private int delay; /// /// /// /// Exception: /// throws Exception protected override void Stop0() { Close( true ); base.Stop0(); } /// /// /// /// Exception: /// throws Exception /// private Socket CheckSocket() { Socket ss = serverSocket; if ( ss == null ) { //Console.WriteLine( "----socket was null----" ); throw new IOException("server socket is closed"); } return ss; } private Socket serverSocket; public override string ToString() { Socket ss = serverSocket; if ( ss == null ) return String.Format("TcpListener (down, {0}:{1})", host, port); return String.Format("TcpListener (up, {0})", ss.LocalEndPoint); } [MethodImpl (MethodImplOptions.Synchronized)] protected override bool OpenSocket( bool reconnect ) { bool first = true; while ( IsStarted() ) { if ( reconnect || !first ) { if ( delay == 0 ) return false; System.Threading.Monitor.Wait(this, delay); if ( !IsStarted() ) break; } try { //IPHostEntry ipEntry = Dns.GetHostEntry( host ); IPAddress addr; if ( host != null ) { IPAddress[] addrs = Dns.GetHostAddresses( host ); if ( addrs == null || addrs.Length == 0 ) throw new ArgumentException( "host is invalid" ); addr = addrs[ 0 ]; } else { addr = IPAddress.Any; } //IPAddress[] addr = ipEntry.AddressList; //string sIPAddress = addr[ 0 ].ToString(); IPEndPoint ipe = new IPEndPoint( addr, port ); serverSocket = new Socket( ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp ); try { serverSocket.Bind(ipe); } catch (SocketException e) { throw new IOException("Could not bind address "+host+":"+port, e); } serverSocket.Listen( backlog ); return true; } catch (Exception e) { if ( first ) { first = false; FireException( "open", e ); } } } return false; } /// /// /// /// Exception: /// throws Exception protected override void SetUpSocket() { // nothing to do. } /// /// /// /// Exception: /// throws Exception protected override void ReadSocket() { try { Socket ss = CheckSocket(); while (IsStarted()) { Socket s = ss.Accept(); try { session.SessionAccepted(s); } catch (Exception e) { s.Close(); FireException("accepted", e); } } } catch (SocketException e) { if (e.Message != null && e.Message.Contains("interrupted")) return; throw e; } } /// /// /// /// /// Exception: /// throws Exception public override void Close( bool reset ) { Socket ss = serverSocket; if ( ss != null ) { serverSocket = null; ss.Close(); } } public override EndPoint LocalAddress() { return CheckSocket().LocalEndPoint; } public override EndPoint RemoteAddress() { // ignore return null; } } }