<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/> <title>GNU CommonC++: tcpservice.cpp</title> <link href="tabs.css" rel="stylesheet" type="text/css"/> <link href="doxygen.css" rel="stylesheet" type="text/css"/> </head> <body> <!-- Generated by Doxygen 1.6.3 --> <div class="navigation" id="top"> <div class="tabs"> <ul> <li><a href="index.html"><span>Main Page</span></a></li> <li><a href="namespaces.html"><span>Namespaces</span></a></li> <li><a href="annotated.html"><span>Classes</span></a></li> <li><a href="files.html"><span>Files</span></a></li> <li><a href="examples.html"><span>Examples</span></a></li> </ul> </div> </div> <div class="contents"> <h1>tcpservice.cpp</h1><div class="fragment"><pre class="fragment"><span class="comment">//</span> <span class="comment">// tcpservice.cpp</span> <span class="comment">//</span> <span class="comment">// Copyright 2000 - Gianni Mariani <gianni@mariani.ws></span> <span class="comment">//</span> <span class="comment">// An example of a simple chatty server using CommonC++.</span> <span class="comment">//</span> <span class="comment">// This simple application basically operates as a</span> <span class="comment">// very simple chat system. From a telnet session</span> <span class="comment">// on localhost:3999 , any messages typed from a telnet</span> <span class="comment">// client are written to all participating sessions.</span> <span class="comment">//</span> <span class="comment">// This is free software licensed under the terms of the GNU</span> <span class="comment">// Public License</span> <span class="comment">//</span> <span class="comment">// This example:</span> <span class="comment">//</span> <span class="comment">// This demostrates a simple threaded server, actually,</span> <span class="comment">// the sessions are not all threaded though they could be</span> <span class="comment">// if that's what you wanted. Basically it demonstrates the</span> <span class="comment">// use of SocketService, SocketPorts and Threads.</span> <span class="comment">//</span> <span class="comment">// For those familiar with Unix network programming, SocketService</span> <span class="comment">// basically encapsulates all the work to communicate with</span> <span class="comment">// the select() or poll() system calls. SocketPorts are</span> <span class="comment">// basically encapsulations of sessions or open file descriptors.</span> <span class="comment">//</span> <span class="comment">// Anyhow, this example is a very simple echo server but</span> <span class="comment">// it echos to all connected clients. So it's a poor man's</span> <span class="comment">// IRC ! You connect via telnet to localhost port 3999 and</span> <span class="comment">// it will echo to all other connected clients what you type in !</span> <span class="comment">//</span> <span class="preprocessor">#include <<a class="code" href="socketport_8h.html" title="Network service framework and design pattern.">cc++/socketport.h</a>></span> <span class="preprocessor">#include <iostream></span> <span class="comment">// For starters, we need a thread safe list, we'll make one</span> <span class="comment">// out of the STL list<> template -</span> <span class="comment">// http://www.sgi.com/Technology/STL/index.html</span> <span class="comment">//</span> <span class="comment">// Thread safe list class</span> <span class="comment">//</span> <span class="preprocessor">#include <list></span> <span class="preprocessor">#ifdef CCXX_NAMESPACES</span> <span class="preprocessor"></span><span class="keyword">using namespace </span>std; <span class="keyword">using namespace </span>ost; <span class="preprocessor">#endif</span> <span class="preprocessor"></span> <span class="keyword">class </span>ts_list_item; <span class="keyword">typedef</span> list<ts_list_item *> ts_list; <span class="comment">// a list head - containing a list and a Mutex.</span> <span class="comment">// It would be really nice to teach stl to do this.</span> <span class="keyword">class </span>ts_list_head { <span class="keyword">public</span>: <span class="comment">// No point inheriting, I'd have to implement</span> <span class="comment">// alot of code. We'll hold off on that exercise.</span> <span class="comment">// Using the CommonC++ Mutex class.</span> <a name="_a0"></a><a class="code" href="classost_1_1_mutex.html" title="The Mutex class is used to protect a section of code so that at any given time only...">Mutex</a> linkmutex; <span class="comment">// And the STL template.</span> ts_list list_o_items; <span class="comment">// Not nessasary, but nice to be explicit.</span> ts_list_head() : linkmutex(), list_o_items() { } <span class="comment">// This thing knows how to remove and insert items.</span> <span class="keywordtype">void</span> RemoveListItem( ts_list_item * li ); <span class="keywordtype">void</span> InsertListItem( ts_list_item * li ); <span class="comment">// And it knows how to notify that it became empty</span> <span class="comment">// or an element was deleted and it was the last one.</span> <span class="keyword">virtual</span> <span class="keywordtype">void</span> ListDepleted() { } <span class="keyword">virtual</span> ~ts_list_head() { } }; <span class="comment">// This item knows how to remove itself from the</span> <span class="comment">// list it belongs to.</span> <span class="keyword">class </span>ts_list_item { <span class="keyword">public</span>: ts_list::iterator linkpoint; ts_list_head * listhead; <span class="keyword">virtual</span> ~ts_list_item() { listhead->RemoveListItem( <span class="keyword">this</span> ); } ts_list_item( ts_list_head * head ) { listhead = head; head->InsertListItem( <span class="keyword">this</span> ); } }; <span class="keywordtype">void</span> ts_list_head::RemoveListItem( ts_list_item * li ) { <span class="keywordtype">bool</span> is_empty; linkmutex.enterMutex(); list_o_items.erase( li->linkpoint ); is_empty = list_o_items.empty(); linkmutex.leaveMutex(); <span class="comment">// There is a slim possibility that at this time</span> <span class="comment">// we recieve a connection.</span> <span class="keywordflow">if</span> ( is_empty ) { ListDepleted(); } } <span class="keywordtype">void</span> ts_list_head::InsertListItem( ts_list_item * li ) { linkmutex.enterMutex(); list_o_items.push_front( li ); li->linkpoint = list_o_items.begin(); linkmutex.leaveMutex(); } <span class="comment">// ChatterSession operates on the individual connections</span> <span class="comment">// from clients as are managed by the SocketService</span> <span class="comment">// contained in CCExec. ChatterThread simply waits in</span> <span class="comment">// a loop to create these, listening forever.</span> <span class="comment">//</span> <span class="comment">// Even though the SocketService contains a list of</span> <span class="comment">// clients it serves, it may actually serve more than</span> <span class="comment">// one type of session so we create our own list by</span> <span class="comment">// inheriting the ts_list_item.</span> <span class="comment">//</span> <span class="keyword">class </span>ChatterSession : <span class="keyword">public</span> <span class="keyword">virtual</span> <a name="_a1"></a><a class="code" href="classost_1_1_socket_port.html" title="The socket port is an internal class which is attached to and then serviced by a...">SocketPort</a>, <span class="keyword">public</span> <span class="keyword">virtual</span> ts_list_item { <span class="keyword">public</span>: <span class="keyword">enum</span> { size_o_buf = 2048 }; <span class="comment">// Nothing special to do here, it's all handled</span> <span class="comment">// by SocketPort and ts_list_item</span> <span class="keyword">virtual</span> ~ChatterSession() { cerr << <span class="stringliteral">"ChatterSession deleted !\n"</span>; } <span class="comment">// When you create a ChatterSession it waits to accept a</span> <span class="comment">// connection. This is done by it's own</span> ChatterSession( <a name="_a2"></a><a class="code" href="classost_1_1_t_c_p_socket.html" title="TCP sockets are used for stream based connected sessions between two sockets.">TCPSocket</a> & server, <a name="_a3"></a><a class="code" href="classost_1_1_socket_service.html" title="The SocketService is a thread pool object that is meant to service attached socket...">SocketService</a> * svc, ts_list_head * head ) : <a class="code" href="classost_1_1_socket_port.html" title="The socket port is an internal class which is attached to and then serviced by a...">SocketPort</a>( NULL, server ), ts_list_item( head ) { cerr << <span class="stringliteral">"ChatterSession Created\n"</span>; <a name="a4"></a><a class="code" href="namespaceost.html#a3c74a1a40c359fd349f3e3e1b96ebfc7" title="Transport Protocol Ports.">tpport_t</a> port; <a name="a5"></a><a class="code" href="address_8h.html#a5eba36aa908d5a479c8ba7d0bc4512b1">InetHostAddress</a> ia = <a name="a6"></a><a class="code" href="classost_1_1_socket.html#a6fd1dffdfe9606e56fba838559ff6d67">getPeer</a>( & port ); cerr << <span class="stringliteral">"connecting from "</span> << ia.getHostname() << <span class="stringliteral">":"</span> << port << endl; <span class="comment">// Set up non-blocking reads</span> <a name="a7"></a><a class="code" href="classost_1_1_socket.html#a16eb72794ce956b2af6b935eb59b2e24" title="Used to specify blocking mode for the socket.">setCompletion</a>( <span class="keyword">false</span> ); <span class="comment">// Set yerself to time out in 10 seconds</span> <a name="a8"></a><a class="code" href="classost_1_1_socket_port.html#ac70f9eae6b6b3625c980fa84180e8151" title="Derived setTimer to notify the service thread pool of change in expected timeout...">setTimer</a>( 100000 ); <a name="a9"></a><a class="code" href="classost_1_1_socket_port.html#aed5fbff0b874da8e24add7f6cc50b65e" title="Attach yourself to the service pool thread object.">attach</a>(svc); } <span class="comment">//</span> <span class="comment">// This is called by the SocketService thread when it the</span> <span class="comment">// object has expired.</span> <span class="comment">//</span> <span class="keyword">virtual</span> <span class="keywordtype">void</span> <a name="a10"></a><a class="code" href="classost_1_1_socket_port.html#a4d09daed3fe5cb81259985db87add34d" title="Called by the service thread pool when the objects timer has expired.">expired</a>() { <span class="comment">// Get outa here - this guy is a LOOSER - type or terminate</span> cerr << <span class="stringliteral">"ChatterSession Expired\n"</span>; <span class="keyword">delete</span> <span class="keyword">this</span>; } <span class="comment">//</span> <span class="comment">// This is called by the SocketService thread when it detects</span> <span class="comment">// that there is somthing to read on this connection.</span> <span class="comment">//</span> <span class="keyword">virtual</span> <span class="keywordtype">void</span> <a name="a11"></a><a class="code" href="classost_1_1_socket_port.html#a5e0ec242cfb737db369cbc63abcc06b6" title="Called by the service thread pool when input data is pending for this socket.">pending</a>() { <span class="comment">// Implement the echo</span> cerr << <span class="stringliteral">"Pending called\n"</span>; <span class="comment">// reset the timer</span> <a class="code" href="classost_1_1_socket_port.html#ac70f9eae6b6b3625c980fa84180e8151" title="Derived setTimer to notify the service thread pool of change in expected timeout...">setTimer</a>( 100000 ); <span class="keywordflow">try</span> { <span class="keywordtype">int</span> len; <span class="keywordtype">unsigned</span> <span class="keywordtype">int</span> total = 0; <span class="keywordtype">char</span> buf[ size_o_buf ]; <span class="keywordflow">while</span> ( (len = <a name="a12"></a><a class="code" href="classost_1_1_socket_port.html#a872c38a65b17113a580af345946146d7" title="Receive a message from any host.">receive</a>(buf, <span class="keyword">sizeof</span>(buf) )) > 0 ) { total += len; cerr << <span class="stringliteral">"Read '"</span>; cerr.write( buf, len ); cerr << <span class="stringliteral">"'\n"</span>; <span class="comment">// Send it to all the sessions.</span> <span class="comment">// We probably don't really want to lock the</span> <span class="comment">// entire list for the entire time.</span> <span class="comment">// The best way to do this would be to place the</span> <span class="comment">// message somewhere and use the service function.</span> <span class="comment">// But what are examples for ?</span> <span class="keywordtype">bool</span> sent = <span class="keyword">false</span>; listhead->linkmutex.enterMutex(); <span class="keywordflow">for</span> ( ts_list::iterator iter = listhead->list_o_items.begin(); iter != listhead->list_o_items.end(); iter ++ ) { ChatterSession * sess = <span class="keyword">dynamic_cast<</span> ChatterSession * <span class="keyword">></span>( * iter ); <span class="keywordflow">if</span> ( sess != <span class="keyword">this</span> ) { sess->send( buf, len ); sent = <span class="keyword">true</span>; } } listhead->linkmutex.leaveMutex(); <span class="keywordflow">if</span> ( ! sent ) { <a name="a13"></a><a class="code" href="classost_1_1_socket_port.html#a9218e4528f5bdf4215200e2040eefe5b" title="Transmit &quot;send&quot; data to a connected peer host.">send</a>( ( <span class="keywordtype">void</span> * ) <span class="stringliteral">"No one else listening\n"</span>, <span class="keyword">sizeof</span>( <span class="stringliteral">"No one else listening\n"</span> ) - 1 ); <a class="code" href="classost_1_1_socket_port.html#a9218e4528f5bdf4215200e2040eefe5b" title="Transmit &quot;send&quot; data to a connected peer host.">send</a>( buf, len ); } } <span class="keywordflow">if</span> (total == 0) { cerr << <span class="stringliteral">"Broken connection!\n"</span> << endl; <span class="keyword">delete</span> <span class="keyword">this</span>; } } <span class="keywordflow">catch</span> ( ... ) { <span class="comment">// somthing wrong happened here !</span> cerr << <span class="stringliteral">"Socket port write sent an exception !\n"</span>; } } <span class="keyword">virtual</span> <span class="keywordtype">void</span> <a name="a14"></a><a class="code" href="classost_1_1_socket_port.html#a7b2a739ee0cb11bc6332010f4cd21552" title="Called by the service thread pool when a disconnect has occured.">disconnect</a>() { <span class="comment">// Called by the SocketService thread when the client</span> <span class="comment">// hangs up.</span> cerr << <span class="stringliteral">"ChatterSession disconnected!\n"</span>; <span class="keyword">delete</span> <span class="keyword">this</span>; } }; <span class="keyword">class </span>ChatterThread; <span class="comment">//</span> <span class="comment">// This is the main application object containing all the</span> <span class="comment">// state for the application. It uses a SocketService object</span> <span class="comment">// (and thread) to do all the work, however, that object could</span> <span class="comment">// theoretically be use by more than one main application.</span> <span class="comment">//</span> <span class="comment">// It creates a ChatterThread to sit and wait for connections</span> <span class="comment">// from clients.</span> <span class="keyword">class </span>CCExec : <span class="keyword">public</span> <span class="keyword">virtual</span> ts_list_head { <span class="keyword">public</span>: <a class="code" href="classost_1_1_socket_service.html" title="The SocketService is a thread pool object that is meant to service attached socket...">SocketService</a> * service; ChatterThread * my_Chatter; <a name="_a15"></a><a class="code" href="classost_1_1_semaphore.html" title="A semaphore is generally used as a synchronization object between multiple threads...">Semaphore</a> mainsem[1]; CCExec():my_Chatter(NULL) { service = <span class="keyword">new</span> <a class="code" href="classost_1_1_socket_service.html" title="The SocketService is a thread pool object that is meant to service attached socket...">SocketService</a>( 0 ); } <span class="keyword">virtual</span> <span class="keywordtype">void</span> ListDepleted(); <span class="comment">// These methods defined later.</span> <span class="keyword">virtual</span> ~CCExec(); <span class="keywordtype">int</span> RunApp( <span class="keywordtype">char</span> * hn = (<span class="keywordtype">char</span> *)<span class="stringliteral">"localhost"</span> ); }; <span class="comment">//</span> <span class="comment">// ChatterThread simply creates ChatterSession all the time until</span> <span class="comment">// it has an error. I suspect you could create as many of these</span> <span class="comment">// as the OS could take.</span> <span class="comment">//</span> <span class="keyword">class </span>ChatterThread : <span class="keyword">public</span> <span class="keyword">virtual</span> <a class="code" href="classost_1_1_t_c_p_socket.html" title="TCP sockets are used for stream based connected sessions between two sockets.">TCPSocket</a>, <span class="keyword">public</span> <span class="keyword">virtual</span> <a name="_a16"></a><a class="code" href="classost_1_1_thread.html" title="Every thread of execution in an application is created by instantiating an object...">Thread</a> { <span class="keyword">public</span>: CCExec * exec; <span class="keywordtype">void</span> <a name="a17"></a><a class="code" href="classost_1_1_thread.html#add7d339d94b8a1ed2b2b0324a95b7e74" title="All threads execute by deriving the Run method of Thread.">run</a> () { <span class="keywordflow">while</span> ( 1 ) { <span class="keywordflow">try</span> { <span class="comment">// new does all the work to accept a new connection</span> <span class="comment">// attach itself to the SocketService AND include</span> <span class="comment">// itself in the CCExec list of sessions.</span> <span class="keyword">new</span> ChatterSession( * ( <a class="code" href="classost_1_1_t_c_p_socket.html" title="TCP sockets are used for stream based connected sessions between two sockets.">TCPSocket</a> * ) <span class="keyword">this</span>, exec->service, exec ); } <span class="keywordflow">catch</span> ( ... ) { <span class="comment">// Bummer - there was an error.</span> cerr << <span class="stringliteral">"ChatterSession create failed\n"</span>; <a name="a18"></a><a class="code" href="classost_1_1_thread.html#acd315d7c877b346970680d739da3e994" title="Used to properly exit from a Thread derived run() or initial() method.">exit</a>(); } } } ChatterThread( <a class="code" href="address_8h.html#a5eba36aa908d5a479c8ba7d0bc4512b1">InetHostAddress</a> & machine, <span class="keywordtype">int</span> port, CCExec * inexec ) : <a class="code" href="classost_1_1_t_c_p_socket.html" title="TCP sockets are used for stream based connected sessions between two sockets.">TCPSocket</a>( machine, port ), <a class="code" href="classost_1_1_thread.html" title="Every thread of execution in an application is created by instantiating an object...">Thread</a>(), exec( inexec ) { <a name="a19"></a><a class="code" href="classost_1_1_thread.html#a2ef198d7aefb93cf9de4fcc6b66c27a2" title="When a new thread is created, it does not begin immediate execution.">start</a>(); } }; <span class="comment">//</span> <span class="comment">// Bug here, this should go ahead and shut down all sessions</span> <span class="comment">// for application. An exercise left to the reader.</span> CCExec::~CCExec() { <span class="comment">// MUST delete my_Chatter first or it may end up using</span> <span class="comment">// a deleted service.</span> <span class="keywordflow">if</span> ( my_Chatter ) <span class="keyword">delete</span> my_Chatter; <span class="comment">// Delete whatever is left.</span> <span class="keyword">delete</span> service; } <span class="comment">//</span> <span class="comment">// Run App would normally read some config file or take some</span> <span class="comment">// parameters about which port to connect to and then</span> <span class="comment">// do that !</span> <span class="keywordtype">int</span> CCExec::RunApp( <span class="keywordtype">char</span> * hn ) { <span class="comment">// which port ?</span> <a class="code" href="address_8h.html#a5eba36aa908d5a479c8ba7d0bc4512b1">InetHostAddress</a> machine( hn ); <span class="keywordflow">if</span> ( machine.isInetAddress() == false ) { cerr << <span class="stringliteral">"machine is not address"</span> << endl; } cerr << <span class="stringliteral">"machine is "</span> << machine.getHostname() << endl; <span class="comment">// Start accepting connections - this will bind to the</span> <span class="comment">// port as well.</span> <span class="keywordflow">try</span> { my_Chatter = <span class="keyword">new</span> ChatterThread( machine, 3999, <span class="keyword">this</span> ); } <span class="keywordflow">catch</span> ( ... ) { cerr << <span class="stringliteral">"Failed to bind\n"</span>; <span class="keywordflow">return</span> <span class="keyword">false</span>; } <span class="keywordflow">return</span> <span class="keyword">true</span>; } <span class="comment">// When there is no one else connected - terminate !</span> <span class="keywordtype">void</span> CCExec::ListDepleted() { mainsem->post(); } <span class="keywordtype">int</span> main( <span class="keywordtype">int</span> argc, <span class="keywordtype">char</span> ** argv ) { CCExec * server; server = <span class="keyword">new</span> CCExec(); <span class="comment">// take the first command line option as a hostname</span> <span class="comment">// to listen to.</span> <span class="keywordflow">if</span> ( argc > 1 ) { server->RunApp( argv[ 1 ] ); } <span class="keywordflow">else</span> { server->RunApp(); } server->mainsem->wait(); <span class="keyword">delete</span> server; <span class="keywordflow">return</span> 0; } </pre></div> </div> <hr class="footer"/><address style="text-align: right;"><small>Generated on Fri Nov 12 09:55:46 2010 for GNU CommonC++ by <a href="http://www.doxygen.org/index.html"> <img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.6.3 </small></address> </body> </html>