Sophie

Sophie

distrib > Mandriva > 2007.0 > i586 > media > contrib-release > by-pkgid > ad1ba1135a9c9eeffc2e538163e00373 > files > 880

libCommonC++2_1.4-devel-1.4.1-1mdv2007.0.i586.rpm

\section{tcpservice.cpp}


\footnotesize\begin{verbatim}1 //
2 // tcpservice.cpp
3 //
4 //  Copyright 2000 - Gianni Mariani <gianni@mariani.ws>
5 //
6 //  An example of a simple chatty server using CommonC++.
7 //
8 //  This simple application basically operates as a
9 //  very simple chat system. From a telnet session
10 //  on localhost:3999 , any messages typed from a telnet
11 //  client are written to all participating sessions.
12 //
13 //  This is free software licensed under the terms of the GNU
14 //  Public License
15 //
16 //  This example:
17 //
18 //  This demostrates a simple threaded server, actually,
19 //  the sessions are not all threaded though they could be
20 //  if that's what you wanted.  Basically it demonstrates the
21 //  use of SocketService, SocketPorts and Threads.
22 //
23 //  For those familiar with Unix network programming, SocketService
24 //  basically encapsulates all the work to communicate with
25 //  the select() or poll() system calls.  SocketPorts are
26 //  basically encapsulations of sessions or open file descriptors.
27 //
28 //  Anyhow, this example is a very simple echo server but
29 //  it echos to all connected clients.  So it's a poor man's
30 //  IRC !  You connect via telnet to localhost port 3999 and
31 //  it will echo to all other connected clients what you type in !
32 //
33 
34 #include <cc++/socketport.h>
35 
36 #include <iostream>
37 
38 // For starters, we need a thread safe list, we'll make one
39 // out of the STL list<> template -
40 //  http://www.sgi.com/Technology/STL/index.html
41 //
42 // Thread safe list class
43 //
44 #include <list>
45 
46 #ifdef  CCXX_NAMESPACES
47 using namespace std;
48 using namespace ost;
49 #endif
50 
51 class ts_list_item;
52 typedef list<ts_list_item *> ts_list;
53 
54 // a list head - containing a list and a Mutex.
55 // It would be really nice to teach stl to do this.
56 
57 class ts_list_head {
58 public:
59 
60     // No point inheriting, I'd have to implement
61     // alot of code. We'll hold off on that exercise.
62 
63     // Using the CommonC++ Mutex class.
64     Mutex                   linkmutex;
65     // And the STL template.
66     ts_list                 list_o_items;
67 
68     // Not nessasary, but nice to be explicit.
69     ts_list_head()
70         : linkmutex(), list_o_items()
71     {
72     }
73 
74     // This thing knows how to remove and insert items.
75     void RemoveListItem( ts_list_item * li );
76     void InsertListItem( ts_list_item * li );
77 
78     // And it knows how to notify that it became empty
79     // or an element was deleted and it was the last one.
80     virtual void ListDepleted()
81     {
82     }
83 
84     virtual ~ts_list_head()
85     {
86     }
87 };
88 
89 
90 // This item knows how to remove itself from the
91 // list it belongs to.
92 class ts_list_item {
93 public:
94     ts_list::iterator      linkpoint;
95     ts_list_head          * listhead;
96 
97     virtual ~ts_list_item()
98     {
99         listhead->RemoveListItem( this );
100     }
101 
102     ts_list_item( ts_list_head * head )
103     {
104         listhead = head;
105         head->InsertListItem( this );
106     }
107 };
108 
109 void ts_list_head::RemoveListItem( ts_list_item * li )
110 {
111     bool    is_empty;
112     linkmutex.enterMutex();
113     list_o_items.erase( li->linkpoint );
114     is_empty = list_o_items.empty();
115     linkmutex.leaveMutex();
116 
117     // There is a slim possibility that at this time
118     // we recieve a connection.
119     if ( is_empty ) {
120         ListDepleted();
121     }
122 }
123 
124 void ts_list_head::InsertListItem( ts_list_item * li )
125 {
126     linkmutex.enterMutex();
127     list_o_items.push_front( li );
128     li->linkpoint = list_o_items.begin();
129     linkmutex.leaveMutex();
130 }
131 
132 // ChatterSession operates on the individual connections
133 // from clients as are managed by the SocketService
134 // contained in CCExec.  ChatterThread simply waits in
135 // a loop to create these, listening forever.
136 //
137 // Even though the SocketService contains a list of
138 // clients it serves, it may actually serve more than
139 // one type of session so we create our own list by
140 // inheriting the ts_list_item.
141 //
142 
143 class ChatterSession :
144     public virtual SocketPort,
145     public virtual ts_list_item
146 {
147 public:
148 
149         enum { size_o_buf = 2048 };
150 
151     // Nothing special to do here, it's all handled
152     // by SocketPort and ts_list_item
153 
154     virtual ~ChatterSession()
155     {
156         cerr << "ChatterSession deleted !\n";
157     }
158 
159     // When you create a ChatterSession it waits to accept a
160     // connection.  This is done by it's own
161     ChatterSession(
162         TCPSocket      & server,
163         SocketService   * svc,
164         ts_list_head    * head
165     ) :
166         SocketPort( NULL, server ),
167         ts_list_item( head )
168     {
169         cerr << "ChatterSession Created\n";
170 
171         tpport_t port;
172         InetHostAddress ia = getPeer( & port );
173 
174         cerr << "connecting from " << ia.getHostname() <<
175         ":" << port << endl;
176 
177         // Set up non-blocking reads
178         setCompletion( false );
179 
180         // Set yerself to time out in 10 seconds
181         setTimer( 100000 );
182         attach(svc);
183     }
184 
185     //
186     // This is called by the SocketService thread when it the
187     // object has expired.
188     //
189 
190     virtual void expired()
191     {
192         // Get outa here - this guy is a LOOSER - type or terminate
193         cerr << "ChatterSession Expired\n";
194         delete this;
195     }
196 
197     //
198     // This is called by the SocketService thread when it detects
199     // that there is somthing to read on this connection.
200     //
201 
202     virtual void pending()
203     {
204         // Implement the echo
205 
206         cerr << "Pending called\n";
207 
208         // reset the timer
209         setTimer( 100000 );
210         try {
211             int    len;
212             unsigned int total = 0;
213             char    buf[ size_o_buf ];
214 
215             while ( (len = receive(buf, sizeof(buf) )) > 0 ) {
216                 total += len;
217                 cerr << "Read '";
218                 cerr.write( buf, len );
219                 cerr << "'\n";
220 
221                 // Send it to all the sessions.
222                 // We probably don't really want to lock the
223                 // entire list for the entire time.
224                 // The best way to do this would be to place the
225                 // message somewhere and use the service function.
226                 // But what are examples for ?
227 
228                 bool sent = false;
229                 listhead->linkmutex.enterMutex();
230                 for (
231                    ts_list::iterator iter = listhead->list_o_items.begin();
232                    iter != listhead->list_o_items.end();
233                    iter ++
234                 ) {
235                    ChatterSession * sess =
236                     dynamic_cast< ChatterSession * >( * iter );
237                    if ( sess != this ) {
238                     sess->send( buf, len );
239                     sent = true;
240                    }
241                 }
242                 listhead->linkmutex.leaveMutex();
243 
244                 if ( ! sent ) {
245                    send(
246                     ( void * ) "No one else listening\n",
247                     sizeof( "No one else listening\n" ) - 1
248                    );
249 
250                    send( buf, len );
251                 }
252             }
253             if (total == 0)
254             {
255                 cerr << "Broken connection!\n" << endl;
256                 delete this;
257             }
258         }
259         catch ( ... )
260         {
261             // somthing wrong happened here !
262             cerr << "Socket port write sent an exception !\n";
263         }
264 
265     }
266 
267     virtual void disconnect()
268     {
269         // Called by the SocketService thread when the client
270         // hangs up.
271         cerr << "ChatterSession disconnected!\n";
272 
273         delete this;
274     }
275 
276 };
277 
278 class ChatterThread;
279 
280 //
281 // This is the main application object containing all the
282 // state for the application.  It uses a SocketService object
283 // (and thread) to do all the work, however, that object could
284 // theoretically be use by more than one main application.
285 //
286 // It creates a ChatterThread to sit and wait for connections
287 // from clients.
288 
289 class CCExec : public virtual ts_list_head {
290 public:
291 
292     SocketService           * service;
293     ChatterThread              * my_Chatter;
294     Semaphore                 mainsem[1];
295 
296     CCExec():my_Chatter(NULL)
297     {
298         service = new SocketService( 0 );
299     }
300 
301     virtual void ListDepleted();
302 
303     // These methods defined later.
304     virtual ~CCExec();
305     int RunApp( char * hn = "localhost" );
306 
307 };
308 
309 //
310 // ChatterThread simply creates ChatterSession all the time until
311 // it has an error.  I suspect you could create as many of these
312 // as the OS could take.
313 //
314 
315 class ChatterThread : public virtual TCPSocket, public virtual Thread {
316 public:
317 
318     CCExec              * exec;
319 
320     void run ()
321     {
322         while ( 1 ) {
323             try {
324                 // new does all the work to accept a new connection
325                 // attach itself to the SocketService AND include
326                 // itself in the CCExec list of sessions.
327                 new ChatterSession(
328                    * ( TCPSocket * ) this,
329                    exec->service,
330                    exec
331                 );
332             }
333             catch ( ... )
334             {
335                 // Bummer - there was an error.
336                 cerr << "ChatterSession create failed\n";
337                 exit();
338             }
339         }
340     }
341 
342     ChatterThread(
343         InetHostAddress & machine,
344         int           port,
345         CCExec         * inexec
346 
347     ) : TCPSocket( machine, port ),
348         Thread(),
349         exec( inexec )
350     {
351             start();
352     }
353 
354 
355 };
356 
357 //
358 // Bug here, this should go ahead and shut down all sessions
359 // for application.  An exercise left to the reader.
360 
361 CCExec::~CCExec()
362 {
363     // MUST delete my_Chatter first or it may end up using
364     // a deleted service.
365     if ( my_Chatter ) delete my_Chatter;
366 
367     // Delete whatever is left.
368     delete service;
369 }
370 
371 //
372 // Run App would normally read some config file or take some
373 // parameters about which port to connect to and then
374 // do that !
375 int CCExec::RunApp( char * hn )
376 {
377     // which port ?
378 
379     InetHostAddress machine( hn );
380 
381     if ( machine.isInetAddress() == false ) {
382         cerr << "machine is not address" << endl;
383     }
384 
385     cerr << "machine is " << machine.getHostname() << endl;
386 
387     // Start accepting connections - this will bind to the
388     // port as well.
389     try {
390         my_Chatter = new ChatterThread(
391             machine,
392             3999,
393             this
394         );
395     }
396     catch ( ... )
397     {
398         cerr << "Failed to bind\n";
399         return false;
400     }
401     
402     return true;
403 }
404 
405 // When there is no one else connected - terminate !
406 void CCExec::ListDepleted()
407 {
408     mainsem->post();
409 }
410 
411 
412 int main( int argc, char ** argv )
413 {
414     CCExec      * server;
415 
416     server = new CCExec();
417 
418     // take the first command line option as a hostname
419     // to listen to.
420     if ( argc > 1 ) {
421         server->RunApp( argv[ 1 ] );
422     } else {
423         server->RunApp();
424     }
425 
426     server->mainsem->wait();
427 
428     delete server;
429 
430     return 0;
431 }
\end{verbatim}
\normalsize