Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

Changes from Version 1 of EchoServer

Show
Ignore:
Author:
JJR (IP: 207.194.78.216)
Timestamp:
02/26/07 19:16:38 (17 years ago)
Comment:

New example

Legend:

Unmodified
Added
Removed
Modified
  • EchoServer

    v0 v1  
     1{{{ 
     2#!d 
     3/** 
     4 * TCP echo server. 
     5 * 
     6 * When started it binds to free local port and listens 
     7 * for incoming connections. 
     8 * 
     9 * It uses several Tango features: 
     10 *  * threads; 
     11 *  * logging; 
     12 *  * containers; 
     13 *  * networking I/O; 
     14 * 
     15 * Contact: 
     16 * dawid.ciezarkiewicz@asn.pl 
     17 */ 
     18 
     19import tango.io.Stdout; 
     20import tango.io.Buffer; 
     21 
     22import tango.core.Thread; 
     23 
     24import tango.net.ServerSocket; 
     25import tango.net.InternetAddress; 
     26import tango.net.Socket; 
     27import tango.net.SocketConduit; 
     28 
     29import tango.text.convert.Format; 
     30 
     31import tango.util.log.ConsoleAppender; 
     32import tango.util.log.Log; 
     33import tango.util.log.Logger; 
     34 
     35import tango.util.collection.LinkSeq; 
     36 
     37/** 
     38 * Each connection will have one instance of this. 
     39 */ 
     40class ClientHandler { 
     41        private Logger logger; 
     42        private SocketConduit socket; 
     43        private void[] buf; 
     44        private static uint GlobalId = 0; 
     45        private uint id; 
     46 
     47 
     48        public this(SocketConduit socket) { 
     49                this.socket = socket; 
     50                id = GlobalId++; 
     51 
     52                logger = Log.getLogger("client.handler." ~ Formatter("{0}", id)); 
     53                logger.info("creating"); 
     54 
     55                socket.getSocket.blocking(false); 
     56                socket.setTimeout(0); 
     57 
     58                /* this should give funny effect */ 
     59                buf = new byte[2]; 
     60        } 
     61 
     62        public ~this() { 
     63                /* get new one, because members are not valid now */ 
     64                auto logger = Log.getLogger("client.handler." ~ Formatter("{0}", id)); 
     65                logger.info("resources disposed"); 
     66        } 
     67 
     68 
     69        /** 
     70         * Will throw error on the end of connection. 
     71         */ 
     72        public void call() { 
     73                /* 
     74                 * XXX: 
     75                 * SocketConduit.read() is broken for a moment 
     76                 * http://www.dsource.org/projects/tango/ticket/279 
     77                 * because of that - all this isn't quite as 
     78                 * it should be, but it works well. 
     79                 */ 
     80                auto ammount = socket.getSocket.receive(buf); 
     81                if (ammount == -1) { 
     82                        /* nothing */ 
     83                } else if ((ammount == IConduit.Eof || ammount == 0)) { 
     84                        logger.info("connection ended"); 
     85                        throw new Exception("connection ended"); 
     86                } else if (ammount > 0) { 
     87                        logger.trace("got " ~ Formatter("{0}", ammount) ~ " bytes"); 
     88                        socket.write(buf[0..ammount]); 
     89                } 
     90 
     91        } 
     92 
     93} 
     94 
     95/** 
     96 * Manages client connections. 
     97 * 
     98 * It's a thread looping over list of clients 
     99 * calling ClientHandler.call() member 
     100 * and removing closed ones. 
     101 */ 
     102class ClientManager { 
     103        private Thread thread; 
     104        private Logger logger; 
     105        LinkSeq!(ClientHandler) clients; 
     106 
     107        public this() { 
     108                clients = new LinkSeq!(ClientHandler); 
     109                logger = Log.getLogger("client.manager"); 
     110 
     111                logger.info("creating thread"); 
     112                thread = new Thread(&this.clientLoop); 
     113 
     114                logger.info("starting thread"); 
     115                thread.start(); 
     116        } 
     117 
     118        private void clientLoop() { 
     119                while (true) { 
     120                        synchronized (clients) { 
     121                                auto to_delete = new LinkSeq!(ClientHandler); 
     122 
     123                                foreach (client; clients) { 
     124                                        try { 
     125                                                client.call(); 
     126                                        } catch (Exception e) { 
     127                                                to_delete.append(client); 
     128                                        } 
     129                                } 
     130 
     131                                foreach (client; to_delete) { 
     132                                        clients.remove(client); 
     133                                } 
     134                        } 
     135                        Thread.sleep(0.1); 
     136                } 
     137        } 
     138 
     139        public void spawn(SocketConduit sc) { 
     140                synchronized (clients) { 
     141                        clients.append(new ClientHandler(sc)); 
     142                } 
     143        } 
     144} 
     145 
     146/** 
     147 * Thread that loops accepting incoming connections 
     148 * and pass them to ClientManager. 
     149 */ 
     150class Server { 
     151        private Thread thread; 
     152        private Logger logger; 
     153        ServerSocket socket; 
     154        ClientManager clientManager; 
     155 
     156        public this(ClientManager cm) 
     157        in { 
     158                assert(cm); 
     159        } body { 
     160                this.clientManager = cm; 
     161 
     162                logger = Log.getLogger("server"); 
     163 
     164                logger.info("creating socket"); 
     165                socket = new ServerSocket(new InternetAddress("0.0.0.0", 0)); 
     166                logger.info("listening on " ~ socket.getSocket().localAddress().toUtf8()); 
     167 
     168 
     169                logger.info("creating thread"); 
     170                thread = new Thread(&this.serverLoop); 
     171 
     172                logger.info("starting thread"); 
     173                thread.start(); 
     174        } 
     175 
     176        private void serverLoop() { 
     177                while (true) { 
     178                        auto client_socket = socket.accept(); 
     179                        clientManager.spawn(client_socket); 
     180                } 
     181        } 
     182 
     183        public void join() { 
     184                thread.join(); 
     185        } 
     186 
     187} 
     188 
     189int main() { 
     190 
     191        Logger logger = Log.getRootLogger(); 
     192        logger.setLevel(); 
     193        logger.addAppender(new ConsoleAppender); 
     194 
     195        auto ch = new ClientManager(); 
     196        auto a = new Server(ch); 
     197        a.join(); 
     198        return 0; 
     199} 
     200