| 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 "TCPListener.hxx"
|
|---|
| 26 | #include <string>
|
|---|
| 27 | //for signals/slots (Callbacks in C++, see http://libsigc.sourceforge.net/
|
|---|
| 28 | #include <sigc++/signal_system.h>
|
|---|
| 29 | using namespace SigC;
|
|---|
| 30 | //for perror
|
|---|
| 31 | #include <stdio.h>
|
|---|
| 32 | //for close
|
|---|
| 33 | #include <unistd.h>
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 | TCPListener::TCPListener (int port_)
|
|---|
| 37 | :port(port_),newReceived(false),comMode(false)//,destroy(false)
|
|---|
| 38 | {
|
|---|
| 39 | socketItself=make_socket(port);
|
|---|
| 40 | //specify that socketItself it's willing to listen only at one client at a time
|
|---|
| 41 |
|
|---|
| 42 | if( listen(socketItself,1)<0){
|
|---|
| 43 | perror("in listen");
|
|---|
| 44 | }
|
|---|
| 45 | pthread_create(&thread,NULL,&TCPListener::Listening,this);
|
|---|
| 46 | pthread_mutex_init(&mutex,NULL);
|
|---|
| 47 | }
|
|---|
| 48 | void * TCPListener::Listening( void* arg)
|
|---|
| 49 | {
|
|---|
| 50 | TCPListener* self=(TCPListener *) arg;
|
|---|
| 51 | #ifdef DEBUG
|
|---|
| 52 | printf("TCPListener: Created Listening thread\n");
|
|---|
| 53 |
|
|---|
| 54 | #endif
|
|---|
| 55 | fd_set active_fd_set, read_fd_set;
|
|---|
| 56 | int i;
|
|---|
| 57 | size_t size;
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 | /* Initialize the set of active sockets. */
|
|---|
| 61 | FD_ZERO (&active_fd_set);
|
|---|
| 62 | FD_SET (self->socketItself, &active_fd_set);
|
|---|
| 63 |
|
|---|
| 64 | while (1)
|
|---|
| 65 | {
|
|---|
| 66 | // pthread_mutex_lock(&(self->mutex));
|
|---|
| 67 | // if(self->destroy) break;
|
|---|
| 68 | // pthread_mutex_unlock(&(self->mutex));
|
|---|
| 69 |
|
|---|
| 70 | /* Block until input arrives on one or more active sockets. */
|
|---|
| 71 | read_fd_set = active_fd_set;
|
|---|
| 72 | if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0)
|
|---|
| 73 | {
|
|---|
| 74 | perror("TCPListener: select");
|
|---|
| 75 | exit (EXIT_FAILURE);
|
|---|
| 76 | }
|
|---|
| 77 |
|
|---|
| 78 | /* Service all the sockets with input pending. */
|
|---|
| 79 | for (i = 0; i < FD_SETSIZE; ++i) {
|
|---|
| 80 |
|
|---|
| 81 | if (FD_ISSET (i, &read_fd_set))
|
|---|
| 82 | {
|
|---|
| 83 | if (i == self->socketItself)
|
|---|
| 84 | {
|
|---|
| 85 | /* Connection request on original socket. */
|
|---|
| 86 | #ifdef DEBUG
|
|---|
| 87 | printf("TCPListener: Accepting connections\n");
|
|---|
| 88 | #endif
|
|---|
| 89 | self->Accept();
|
|---|
| 90 | //monitor the created channel
|
|---|
| 91 | FD_SET (self->channel, &active_fd_set);
|
|---|
| 92 | //but since we don't more than two clients, don't monitor incoming_connections_fd
|
|---|
| 93 | FD_CLR (self->socketItself, &active_fd_set);
|
|---|
| 94 | }
|
|---|
| 95 | else
|
|---|
| 96 | {
|
|---|
| 97 |
|
|---|
| 98 | /* Data arriving on an already-connected socket. */
|
|---|
| 99 | #ifdef DEBUG
|
|---|
| 100 | printf("TCPListener: Received\n");
|
|---|
| 101 | #endif
|
|---|
| 102 |
|
|---|
| 103 |
|
|---|
| 104 | self->Receive();
|
|---|
| 105 | if(!self->comMode){ //if connection has been closed to client (after the Receive), until new connection is open don't monitor this
|
|---|
| 106 | FD_CLR (self->channel, &active_fd_set);
|
|---|
| 107 | //monitor incoming_connections_fd back again
|
|---|
| 108 | FD_SET (self->socketItself, &active_fd_set);
|
|---|
| 109 | }
|
|---|
| 110 |
|
|---|
| 111 | }
|
|---|
| 112 | }
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | }
|
|---|
| 116 | #ifdef DEBUG
|
|---|
| 117 | printf("TCPListener: Listening thread finishing\n");
|
|---|
| 118 |
|
|---|
| 119 | #endif
|
|---|
| 120 |
|
|---|
| 121 | }
|
|---|
| 122 |
|
|---|
| 123 |
|
|---|
| 124 | TCPListener::~TCPListener() {
|
|---|
| 125 | if(comMode){
|
|---|
| 126 | #ifdef DEBUG
|
|---|
| 127 | printf("TCPListener: deleting\n");
|
|---|
| 128 | #endif
|
|---|
| 129 | this->ClosingChannel();
|
|---|
| 130 | }
|
|---|
| 131 | // pthread_mutex_lock(&mutex);
|
|---|
| 132 | // destroy=true;
|
|---|
| 133 | // pthread_mutex_unlock(&mutex);
|
|---|
| 134 | // pthread_join(thread,NULL);
|
|---|
| 135 | pthread_cancel(thread);
|
|---|
| 136 |
|
|---|
| 137 | close(socketItself);
|
|---|
| 138 |
|
|---|
| 139 | }
|
|---|
| 140 |
|
|---|
| 141 | void TCPListener::Accept () {
|
|---|
| 142 | size_t size=sizeof(socketAddr);
|
|---|
| 143 | channel=accept(socketItself, (struct sockaddr *) &socketAddr, &size);
|
|---|
| 144 | if(channel<0){
|
|---|
| 145 | perror("in Accept");
|
|---|
| 146 | // exit (EXIT_FAILURE);
|
|---|
| 147 | }else{
|
|---|
| 148 | clientAddress=inet_ntoa(socketAddr.sin_addr);
|
|---|
| 149 | clientPort=ntohs(socketAddr.sin_port);
|
|---|
| 150 | #ifdef DEBUG
|
|---|
| 151 | fprintf(stderr, "TCPListener: client contacted from %s:%hd\n", clientAddress, clientPort);
|
|---|
| 152 | #endif
|
|---|
| 153 | // fconfigure $channel -buffering line
|
|---|
| 154 | comMode=true;
|
|---|
| 155 |
|
|---|
| 156 | }
|
|---|
| 157 | }
|
|---|
| 158 |
|
|---|
| 159 |
|
|---|
| 160 | //this next method is only internally called when something arrives at channel (be careful, it can be the connection has closed)
|
|---|
| 161 |
|
|---|
| 162 | 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
|
|---|
| 163 | #ifdef DEBUG
|
|---|
| 164 | printf("TCPListener: Receive from TCPListener %d\n", this);
|
|---|
| 165 | #endif
|
|---|
| 166 | int nbytes;
|
|---|
| 167 | nbytes = read (channel, receivedStream, MAXMSG);
|
|---|
| 168 | if (nbytes < 0){
|
|---|
| 169 | /* Read error. */
|
|---|
| 170 | perror("TCPListener: read");
|
|---|
| 171 | exit (EXIT_FAILURE);
|
|---|
| 172 | }else if (nbytes == 0){
|
|---|
| 173 |
|
|---|
| 174 | /* End-of-file. */
|
|---|
| 175 | receivedStream[0]='0';
|
|---|
| 176 | receivedStream[1]='\0';
|
|---|
| 177 | this->ClosingChannel();
|
|---|
| 178 | }else{
|
|---|
| 179 | /* Data read. */
|
|---|
| 180 | //look for '\n' and chomp, in anycase read doesn't supply the \n so add it
|
|---|
| 181 | if(receivedStream[nbytes-1]=='\n'){
|
|---|
| 182 | receivedStream[nbytes-1]='\0';
|
|---|
| 183 | #ifdef DEBUG
|
|---|
| 184 | printf("TCPListener: chomp\n");
|
|---|
| 185 | #endif
|
|---|
| 186 | }else{
|
|---|
| 187 | receivedStream[nbytes]='\0';
|
|---|
| 188 | }
|
|---|
| 189 | #ifdef DEBUG
|
|---|
| 190 | printf("TCPListener: got message: `%s'\n", receivedStream);
|
|---|
| 191 | #endif
|
|---|
| 192 | this->process();
|
|---|
| 193 | newReceived=true;
|
|---|
| 194 | }
|
|---|
| 195 | }
|
|---|
| 196 |
|
|---|
| 197 | void TCPListener::ClosingChannel(){
|
|---|
| 198 | #ifdef DEBUG
|
|---|
| 199 | printf("TCPListener: closing socket to client from %d:%d\n",clientAddress,clientPort);
|
|---|
| 200 | #endif
|
|---|
| 201 |
|
|---|
| 202 | close(channel);
|
|---|
| 203 | comMode=false;
|
|---|
| 204 |
|
|---|
| 205 | }
|
|---|
| 206 | //this public method should be capitalized for some consistency. Put the change should be propagated. class Make up!
|
|---|
| 207 | void TCPListener::process (){
|
|---|
| 208 | //to be overriden
|
|---|
| 209 | #ifdef DEBUG
|
|---|
| 210 | printf("TCPListener: message Received [%s] and processed from client %s:%d \n",receivedStream,clientAddress,clientPort);
|
|---|
| 211 | #endif
|
|---|
| 212 |
|
|---|
| 213 | }
|
|---|
| 214 |
|
|---|
| 215 | string TCPListener::ReturnNew () {
|
|---|
| 216 | if (newReceived) {
|
|---|
| 217 | newReceived=false;
|
|---|
| 218 | return string(receivedStream);
|
|---|
| 219 | }else{
|
|---|
| 220 | return string("0");
|
|---|
| 221 | }
|
|---|
| 222 | }
|
|---|
| 223 |
|
|---|
| 224 |
|
|---|