00001 /******************************************************************************* 00002 00003 @file SocketListener.d 00004 00005 Copyright (C) 2004 Kris Bell 00006 00007 This software is provided 'as-is', without any express or implied 00008 warranty. In no event will the authors be held liable for damages 00009 of any kind arising from the use of this software. 00010 00011 Permission is hereby granted to anyone to use this software for any 00012 purpose, including commercial applications, and to alter it and/or 00013 redistribute it freely, subject to the following restrictions: 00014 00015 1. The origin of this software must not be misrepresented; you must 00016 not claim that you wrote the original software. If you use this 00017 software in a product, an acknowledgment within documentation of 00018 said product would be appreciated but is not required. 00019 00020 2. Altered source versions must be plainly marked as such, and must 00021 not be misrepresented as being the original software. 00022 00023 3. This notice may not be removed or altered from any distribution 00024 of the source. 00025 00026 00027 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00028 00029 00030 @version Initial version, June 2004 00031 @author Kris 00032 00033 00034 *******************************************************************************/ 00035 00036 module mango.io.SocketListener; 00037 00038 private import std.thread; 00039 00040 private import mango.io.Socket; 00041 00042 private import mango.io.model.IBuffer, 00043 mango.io.model.IWriter; 00044 00045 /****************************************************************************** 00046 00047 Abstract class to asynchronously listen for incoming data on a 00048 socket. This can be used with DatagramSocket & MulticastSocket, 00049 and might possibly be useful with a basic SocketConduit also. 00050 Note that DatagramSocket must first be bound to a local network 00051 address via bind(), and MulticastSocket should first be made a 00052 member of a multicast group via its join() method. Note also 00053 that the underlying thread is not started by the constructor; 00054 you should do that manually via the start() method. 00055 00056 ******************************************************************************/ 00057 00058 //version = AltListener; 00059 00060 version (AltListener) 00061 alias Object FOO; 00062 else 00063 alias Thread FOO; 00064 00065 class SocketListener : FOO, IListener 00066 { 00067 private bool quit; 00068 private IBuffer buffer; 00069 private ISocketReader reader; 00070 private int limit = 3; 00071 00072 /********************************************************************** 00073 00074 Construct a listener with the requisite arguments. The 00075 specified buffer is populated via the provided instance 00076 of ISocketReader before being passed to the notify() 00077 method. All arguments are required. 00078 00079 **********************************************************************/ 00080 00081 this (ISocketReader reader, IBuffer buffer) 00082 in { 00083 assert (reader); 00084 assert (buffer); 00085 } 00086 body 00087 { 00088 this.buffer = buffer; 00089 this.reader = reader; 00090 version (AltListener) 00091 { 00092 Thread t = new Thread (&run); 00093 t.start(); 00094 } 00095 } 00096 00097 version (AltListener) 00098 void start(){} 00099 00100 /*********************************************************************** 00101 00102 Notification callback invoked whenever the listener has 00103 anything to report. The buffer will have whatever content 00104 was available from the read() operation 00105 00106 ***********************************************************************/ 00107 00108 abstract void notify (IBuffer buffer); 00109 00110 /*********************************************************************** 00111 00112 Handle error conditions from the listener thread. 00113 00114 ***********************************************************************/ 00115 00116 abstract void exception (char[] msg); 00117 00118 /********************************************************************** 00119 00120 Cancel this listener. The thread will quit only after the 00121 current read() request responds, or is interrrupted. 00122 00123 **********************************************************************/ 00124 00125 void cancel () 00126 { 00127 quit = true; 00128 } 00129 00130 /********************************************************************** 00131 00132 Set the maximum contiguous number of exceptions this 00133 listener will survive. Setting a limit of zero will 00134 not survive any errors at all, whereas a limit of two 00135 will survive as long as two consecutive errors don't 00136 arrive back to back. 00137 00138 **********************************************************************/ 00139 00140 void setErrorLimit (ushort limit) 00141 { 00142 this.limit = limit + 1; 00143 } 00144 00145 /********************************************************************** 00146 00147 Execution of this thread is typically stalled on the 00148 read() method belonging to the ISocketReader specified 00149 during construction. You can invoke cancel() to indicate 00150 execution should not proceed further, but that will not 00151 actually interrupt a blocked read() operation. 00152 00153 Note that exceptions are all directed towards the handler 00154 implemented by the class instance. 00155 00156 **********************************************************************/ 00157 00158 int run () 00159 { 00160 int lives = limit; 00161 00162 while (lives > 0) 00163 try { 00164 // start with a clean slate 00165 buffer.clear (); 00166 00167 // wait for incoming content 00168 reader.read (buffer); 00169 00170 // time to quit? Note that a v0.95 compiler bug 00171 // prohibits 'break' from exiting the try{} block 00172 if (quit || Socket.isCancelled ()) 00173 lives = 0; 00174 else 00175 { 00176 // invoke callback 00177 notify (buffer); 00178 lives = limit; 00179 } 00180 } catch (Object x) 00181 // time to quit? 00182 if (quit || Socket.isCancelled ()) 00183 break; 00184 else 00185 { 00186 exception (x.toString); 00187 if (--lives == 0) 00188 exception ("listener thread aborting"); 00189 } 00190 version (TraceLinux) 00191 { 00192 printf ("SocketListener exiting\n"); 00193 } 00194 return 0; 00195 } 00196 } 00197 00198 00199