📄 tcpservice.cpp
字号:
//// tcpservice.cpp//// Copyright 2000 - Gianni Mariani <gianni@mariani.ws>//// An example of a simple chatty server using CommonC++.//// This simple application basically operates as a// very simple chat system. From a telnet session// on localhost:3999 , any messages typed from a telnet// client are written to all participating sessions.//// This is free software licensed under the terms of the GNU// Public License//// This example://// This demostrates a simple threaded server, actually,// the sessions are not all threaded though they could be// if that's what you wanted. Basically it demonstrates the// use of SocketService, SocketPorts and Threads.//// For those familiar with Unix network programming, SocketService// basically encapsulates all the work to communicate with// the select() or poll() system calls. SocketPorts are// basically encapsulations of sessions or open file descriptors.//// Anyhow, this example is a very simple echo server but// it echos to all connected clients. So it's a poor man's// IRC ! You connect via telnet to localhost port 3999 and// it will echo to all other connected clients what you type in !//#include <cc++/socketport.h>#include <iostream>// For starters, we need a thread safe list, we'll make one// out of the STL list<> template -// http://www.sgi.com/Technology/STL/index.html//// Thread safe list class//#include <list>#ifdef CCXX_NAMESPACESusing namespace std;using namespace ost;#endifclass ts_list_item;typedef list<ts_list_item *> ts_list;// a list head - containing a list and a Mutex.// It would be really nice to teach stl to do this.class ts_list_head {public: // No point inheriting, I'd have to implement // alot of code. We'll hold off on that exercise. // Using the CommonC++ Mutex class. Mutex linkmutex; // And the STL template. ts_list list_o_items; // Not nessasary, but nice to be explicit. ts_list_head() : linkmutex(), list_o_items() { } // This thing knows how to remove and insert items. void RemoveListItem( ts_list_item * li ); void InsertListItem( ts_list_item * li ); // And it knows how to notify that it became empty // or an element was deleted and it was the last one. virtual void ListDepleted() { } virtual ~ts_list_head() { }};// This item knows how to remove itself from the// list it belongs to.class ts_list_item {public: ts_list::iterator linkpoint; ts_list_head * listhead; virtual ~ts_list_item() { listhead->RemoveListItem( this ); } ts_list_item( ts_list_head * head ) { listhead = head; head->InsertListItem( this ); }};void ts_list_head::RemoveListItem( ts_list_item * li ){ bool is_empty; linkmutex.enterMutex(); list_o_items.erase( li->linkpoint ); is_empty = list_o_items.empty(); linkmutex.leaveMutex(); // There is a slim possibility that at this time // we recieve a connection. if ( is_empty ) { ListDepleted(); }}void ts_list_head::InsertListItem( ts_list_item * li ){ linkmutex.enterMutex(); list_o_items.push_front( li ); li->linkpoint = list_o_items.begin(); linkmutex.leaveMutex();}// ChatterSession operates on the individual connections// from clients as are managed by the SocketService// contained in CCExec. ChatterThread simply waits in// a loop to create these, listening forever.//// Even though the SocketService contains a list of// clients it serves, it may actually serve more than// one type of session so we create our own list by// inheriting the ts_list_item.//class ChatterSession : public virtual SocketPort, public virtual ts_list_item{public: enum { size_o_buf = 2048 }; // Nothing special to do here, it's all handled // by SocketPort and ts_list_item virtual ~ChatterSession() { cerr << "ChatterSession deleted !\n"; } // When you create a ChatterSession it waits to accept a // connection. This is done by it's own ChatterSession( TCPSocket & server, SocketService * svc, ts_list_head * head ) : SocketPort( NULL, server ), ts_list_item( head ) { cerr << "ChatterSession Created\n"; tpport_t port; InetHostAddress ia = getPeer( & port ); cerr << "connecting from " << ia.getHostname() << ":" << port << endl; // Set up non-blocking reads setCompletion( false ); // Set yerself to time out in 10 seconds setTimer( 100000 ); attach(svc); } // // This is called by the SocketService thread when it the // object has expired. // virtual void expired() { // Get outa here - this guy is a LOOSER - type or terminate cerr << "ChatterSession Expired\n"; delete this; } // // This is called by the SocketService thread when it detects // that there is somthing to read on this connection. // virtual void pending() { // Implement the echo cerr << "Pending called\n"; // reset the timer setTimer( 100000 ); try { int len; unsigned int total = 0; char buf[ size_o_buf ]; while ( (len = receive(buf, sizeof(buf) )) > 0 ) { total += len; cerr << "Read '"; cerr.write( buf, len ); cerr << "'\n"; // Send it to all the sessions. // We probably don't really want to lock the // entire list for the entire time. // The best way to do this would be to place the // message somewhere and use the service function. // But what are examples for ? bool sent = false; listhead->linkmutex.enterMutex(); for ( ts_list::iterator iter = listhead->list_o_items.begin(); iter != listhead->list_o_items.end(); iter ++ ) { ChatterSession * sess = dynamic_cast< ChatterSession * >( * iter ); if ( sess != this ) { sess->send( buf, len ); sent = true; } } listhead->linkmutex.leaveMutex(); if ( ! sent ) { send( ( void * ) "No one else listening\n", sizeof( "No one else listening\n" ) - 1 ); send( buf, len ); } } if (total == 0) { cerr << "Broken connection!\n" << endl; delete this; } } catch ( ... ) { // somthing wrong happened here ! cerr << "Socket port write sent an exception !\n"; } } virtual void disconnect() { // Called by the SocketService thread when the client // hangs up. cerr << "ChatterSession disconnected!\n"; delete this; }};class ChatterThread;//// This is the main application object containing all the// state for the application. It uses a SocketService object// (and thread) to do all the work, however, that object could// theoretically be use by more than one main application.//// It creates a ChatterThread to sit and wait for connections// from clients.class CCExec : public virtual ts_list_head {public: SocketService * service; ChatterThread * my_Chatter; Semaphore mainsem[1]; CCExec():my_Chatter(NULL) { service = new SocketService( 0 ); } virtual void ListDepleted(); // These methods defined later. virtual ~CCExec(); int RunApp( char * hn = "localhost" );};//// ChatterThread simply creates ChatterSession all the time until// it has an error. I suspect you could create as many of these// as the OS could take.//class ChatterThread : public virtual TCPSocket, public virtual Thread {public: CCExec * exec; void run () { while ( 1 ) { try { // new does all the work to accept a new connection // attach itself to the SocketService AND include // itself in the CCExec list of sessions. new ChatterSession( * ( TCPSocket * ) this, exec->service, exec ); } catch ( ... ) { // Bummer - there was an error. cerr << "ChatterSession create failed\n"; exit(); } } } ChatterThread( InetHostAddress & machine, int port, CCExec * inexec ) : TCPSocket( machine, port ), Thread(), exec( inexec ) { start(); }};//// Bug here, this should go ahead and shut down all sessions// for application. An exercise left to the reader.CCExec::~CCExec(){ // MUST delete my_Chatter first or it may end up using // a deleted service. if ( my_Chatter ) delete my_Chatter; // Delete whatever is left. delete service;}//// Run App would normally read some config file or take some// parameters about which port to connect to and then// do that !int CCExec::RunApp( char * hn ){ // which port ? InetHostAddress machine( hn ); if ( machine.isInetAddress() == false ) { cerr << "machine is not address" << endl; } cerr << "machine is " << machine.getHostname() << endl; // Start accepting connections - this will bind to the // port as well. try { my_Chatter = new ChatterThread( machine, 3999, this ); } catch ( ... ) { cerr << "Failed to bind\n"; return false; } return true;}// When there is no one else connected - terminate !void CCExec::ListDepleted(){ mainsem->post();}int main( int argc, char ** argv ){ CCExec * server; server = new CCExec(); // take the first command line option as a hostname // to listen to. if ( argc > 1 ) { server->RunApp( argv[ 1 ] ); } else { server->RunApp(); } server->mainsem->wait(); delete server; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -