| 1 | /*  Copyright (C) 2001 Marc Casaldaliga Albisu <casaldaliga@ifae.es> | 
|---|
| 2 | ================================================================ | 
|---|
| 3 |  | 
|---|
| 4 | This code is free software; you can redistribute it and/or modify | 
|---|
| 5 | it under the terms of the GNU General Public License as published by | 
|---|
| 6 | the Free Software Foundation; either version 2 of the License, or | 
|---|
| 7 | (at your option) any later version. | 
|---|
| 8 |  | 
|---|
| 9 | This code is distributed in the hope that it will be useful, | 
|---|
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 12 | GNU General Public License for more details. | 
|---|
| 13 |  | 
|---|
| 14 | You should have received a copy of the GNU General Public License | 
|---|
| 15 | along with Emacs (which is required to make this stuff work); if | 
|---|
| 16 | not, write to the Free Software Foundation, Inc., 675 Mass Ave, | 
|---|
| 17 | Cambridge, MA 02139, USA. | 
|---|
| 18 | ================================================================== | 
|---|
| 19 | */ | 
|---|
| 20 |  | 
|---|
| 21 | #include <sys/socket.h> | 
|---|
| 22 | #include <netinet/in.h> | 
|---|
| 23 | #include <arpa/inet.h> | 
|---|
| 24 | #include "socket_functions.h" | 
|---|
| 25 | #include "IONotifier.hxx" | 
|---|
| 26 | #include "TCPListener.hxx" | 
|---|
| 27 | #include <string> | 
|---|
| 28 | //for signals/slots (Callbacks in C++, see http://libsigc.sourceforge.net/ | 
|---|
| 29 | #include <sigc++/signal_system.h> | 
|---|
| 30 | using namespace SigC; | 
|---|
| 31 | //for perror | 
|---|
| 32 | #include <stdio.h> | 
|---|
| 33 | //for close | 
|---|
| 34 | #include <unistd.h> | 
|---|
| 35 |  | 
|---|
| 36 |  | 
|---|
| 37 | TCPListener::TCPListener (int port_) | 
|---|
| 38 | :port(port_),newReceived(false),comMode(false),notifierOfIncomingConn(0),channelReadable(0) | 
|---|
| 39 | { | 
|---|
| 40 | socketItself=make_socket(port); | 
|---|
| 41 | //specify that socketItself it's willing to listen only at one client at a time | 
|---|
| 42 |  | 
|---|
| 43 | if( listen(socketItself,1)<0){ | 
|---|
| 44 | perror("in listen"); | 
|---|
| 45 | } | 
|---|
| 46 | notifierOfIncomingConn=new IONotifier(socketItself); | 
|---|
| 47 | //when notifierOfIncomingConn becomes readable it's because it is recieving an incoming connection, Accept it with accept method. The next statment connects this readable signal to accept slot | 
|---|
| 48 | notifierOfIncomingConn->readable.connect(slot(*this,&TCPListener::Accept)); | 
|---|
| 49 |  | 
|---|
| 50 | } | 
|---|
| 51 |  | 
|---|
| 52 | TCPListener::~TCPListener() { | 
|---|
| 53 | if(comMode){ | 
|---|
| 54 | cerr<<"deleting\n"; | 
|---|
| 55 |  | 
|---|
| 56 | this->ClosingChannel(); | 
|---|
| 57 | } | 
|---|
| 58 | delete notifierOfIncomingConn; | 
|---|
| 59 | close(socketItself); | 
|---|
| 60 | } | 
|---|
| 61 |  | 
|---|
| 62 | void TCPListener::Accept () { | 
|---|
| 63 | size_t size=sizeof(socketAddr); | 
|---|
| 64 | channel=accept(socketItself, (struct sockaddr *) &socketAddr, &size); | 
|---|
| 65 | if(channel<0){ | 
|---|
| 66 | perror("in Accept"); | 
|---|
| 67 | //            exit (EXIT_FAILURE); | 
|---|
| 68 | }else{ | 
|---|
| 69 | //first of all, don't acccept new connections | 
|---|
| 70 | delete notifierOfIncomingConn; | 
|---|
| 71 |  | 
|---|
| 72 | clientAddress=inet_ntoa(socketAddr.sin_addr); | 
|---|
| 73 | clientPort=ntohs(socketAddr.sin_port); | 
|---|
| 74 | fprintf(stderr, "TCPListener: client contacted from %s:%hd\n", clientAddress, clientPort); | 
|---|
| 75 | //            fconfigure $channel -buffering line | 
|---|
| 76 | // assume channel opens a O_SYNC | 
|---|
| 77 | channelReadable=new IONotifier(channel); | 
|---|
| 78 | channelReadable->readable.connect(slot(this,&TCPListener::Receive)); | 
|---|
| 79 | comMode=true; | 
|---|
| 80 |  | 
|---|
| 81 | } | 
|---|
| 82 | } | 
|---|
| 83 |  | 
|---|
| 84 |  | 
|---|
| 85 | //this next method is only internally called when something arrives at channel (be careful, it can be the connection has closed) | 
|---|
| 86 | void TCPListener::Receive () { //Syncronous receive. If you are not sure there will be data to read, schedule this receive with IONotifier or something. Otherwise it will block | 
|---|
| 87 | //      cerr << "Receive\n" | 
|---|
| 88 | int nbytes; | 
|---|
| 89 | nbytes = read (channel, receivedStream, MAXMSG); | 
|---|
| 90 | if (nbytes < 0){ | 
|---|
| 91 | /* Read error. */ | 
|---|
| 92 | perror("TCPListener: read"); | 
|---|
| 93 | exit (EXIT_FAILURE); | 
|---|
| 94 | }else if (nbytes == 0){ | 
|---|
| 95 |  | 
|---|
| 96 | /* End-of-file. */ | 
|---|
| 97 | receivedStream[0]='0'; | 
|---|
| 98 | receivedStream[1]='\0'; | 
|---|
| 99 | this->ClosingChannel(); | 
|---|
| 100 | }else{ | 
|---|
| 101 | /* Data read. */ | 
|---|
| 102 | //look for '\n' and chomp, in anycase read doesn't supply the \n so add it | 
|---|
| 103 | if(receivedStream[nbytes-1]=='\n'){ | 
|---|
| 104 | receivedStream[nbytes-1]='\0'; | 
|---|
| 105 | #ifdef DEBUG | 
|---|
| 106 | printf("TCPListener: chomp\n"); | 
|---|
| 107 | #endif | 
|---|
| 108 | }else{ | 
|---|
| 109 | receivedStream[nbytes]='\0'; | 
|---|
| 110 | } | 
|---|
| 111 | #ifdef DEBUG | 
|---|
| 112 | printf("TCPListener: got message: `%s'\n", receivedStream); | 
|---|
| 113 | #endif | 
|---|
| 114 | this->process(); | 
|---|
| 115 | newReceived=true; | 
|---|
| 116 | } | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | void TCPListener::ClosingChannel(){ | 
|---|
| 120 | cerr<<"TCPListener: closing socket to client from "<<clientAddress<<":"<<clientPort<<"\n"; | 
|---|
| 121 | delete channelReadable; | 
|---|
| 122 | close(channel); | 
|---|
| 123 | comMode=false; | 
|---|
| 124 | //setup again | 
|---|
| 125 | notifierOfIncomingConn=new IONotifier(socketItself); | 
|---|
| 126 | //when notifierOfIncomingConn becomes readable it's because it is recieving an incoming connection, Accept it with accept method. The next statment connects this readable signal to accept slot | 
|---|
| 127 | notifierOfIncomingConn->readable.connect(slot(*this,&TCPListener::Accept)); | 
|---|
| 128 |  | 
|---|
| 129 | } | 
|---|
| 130 | //this public method should be capitalized for some consistency. Put the change should be propagated. class Make up! | 
|---|
| 131 | void TCPListener::process (){ | 
|---|
| 132 | //to be overriden | 
|---|
| 133 | cerr<<"TCPListener: message Received ["<<receivedStream<<"] and processed from "<<clientAddress<<":"<<clientPort<<"\n"; | 
|---|
| 134 |  | 
|---|
| 135 | } | 
|---|
| 136 |  | 
|---|
| 137 | string TCPListener::ReturnNew () { | 
|---|
| 138 | if (newReceived) { | 
|---|
| 139 | newReceived=false; | 
|---|
| 140 | return string(receivedStream); | 
|---|
| 141 | }else{ | 
|---|
| 142 | return string("0"); | 
|---|
| 143 | } | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 |  | 
|---|