OPeNDAP Hyrax Back End Server (BES)  Updated for version 3.8.3
SocketListener.cc
Go to the documentation of this file.
1 // SocketListener.cc
2 
3 // This file is part of bes, A C++ back-end server implementation framework
4 // for the OPeNDAP Data Access Protocol.
5 
6 // Copyright (c) 2004-2009 University Corporation for Atmospheric Research
7 // Author: Patrick West <pwest@ucar.edu> and Jose Garcia <jgarcia@ucar.edu>
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 // You can contact University Corporation for Atmospheric Research at
24 // 3080 Center Green Drive, Boulder, CO 80301
25 
26 // (c) COPYRIGHT University Corporation for Atmospheric Research 2004-2005
27 // Please read the full copyright statement in the file COPYRIGHT_UCAR.
28 //
29 // Authors:
30 // pwest Patrick West <pwest@ucar.edu>
31 // jgarcia Jose Garcia <jgarcia@ucar.edu>
32 
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/socket.h>
36 
37 // Added for CentOS 6 jhrg
38 #include <sys/wait.h>
39 
40 // Added for OSX 10.9 jhrg
41 #include <sys/select.h>
42 
43 #include <cstring>
44 #include <cerrno>
45 
46 #include "SocketListener.h"
47 #include "BESInternalError.h"
48 #include "Socket.h"
49 #include "SocketConfig.h"
50 #include "BESDebug.h"
51 
52 //extern volatile int bes_num_children; // defined in PPTServer.cc jhrg 3/5/14
53 //extern string bes_exit_message(int cpid, int stat);
54 
56  _accepting(false)
57 {
58 }
59 
61 {
62 }
63 
65 {
66  if (_accepting)
67  throw BESInternalError("Already accepting connections, no more sockets can be added", __FILE__, __LINE__);
68 
69  if (s && !s->isConnected() && !s->isListening()) {
70  s->listen();
71  _socket_list[s->getSocketDescriptor()] = s;
72  }
73  else {
74  if (!s)
75  throw BESInternalError("null socket passed", __FILE__, __LINE__);
76  else if (s->isConnected())
77  throw BESInternalError("socket already connected, cannot listen", __FILE__, __LINE__);
78  else if (s->isListening())
79  throw BESInternalError("socket already listening", __FILE__, __LINE__);
80  }
81 }
82 
84 Socket *
86 {
87  BESDEBUG("ppt", "SocketListener::accept() - START" << endl);
88 
89  fd_set read_fd;
90  FD_ZERO(&read_fd);
91 
92  int maxfd = 0;
93  for (Socket_citer i = _socket_list.begin(), e = _socket_list.end(); i != e; i++) {
94  Socket *s_ptr = (*i).second;
95  if (s_ptr->getSocketDescriptor() > maxfd) maxfd = s_ptr->getSocketDescriptor();
96  FD_SET(s_ptr->getSocketDescriptor(), &read_fd);
97  }
98 #if 0
99  // Without this loop one client will always get an error like:
100  // Could not connect to host localhost on port 10022. Connection reset by peer.
101  // when the server is first started. This may be an artifact of the test client
102  // when it is used to make many (e.g., 20) simultaneous requests. jhrg 3/5/14
103  pid_t cpid;
104  int stat;
105  while ((cpid = wait4(0 /*any child in the process group*/, &stat, WNOHANG, 0/*no rusage*/)) > 0) {
106  --bes_num_children;
107  BESDEBUG("ppt2", bes_exit_message(cpid, stat) << "; num children: " << bes_num_children << endl);
108  }
109 #endif
110  struct timeval timeout;
111  timeout.tv_sec = 120;
112  timeout.tv_usec = 0;
113  while (select(maxfd + 1, &read_fd, (fd_set*) NULL, (fd_set*) NULL, &timeout) < 0) {
114  switch (errno) {
115  case EAGAIN: // rerun select on interrupted calls, ...
116  BESDEBUG("ppt2", "SocketListener::accept() - select encountered EAGAIN" << endl);
117  // This case and the one below used to just 'break' so that the select call
118  // above would run again. I modified it to return null so that the caller could
119  // do other things, like process the results of signals.
120  return 0;
121 
122  case EINTR:
123  BESDEBUG("ppt2", "SocketListener::accept() - select encountered EINTR" << endl);
124  return 0;
125 
126  default:
127  throw BESInternalError(string("select: ") + strerror(errno), __FILE__, __LINE__);
128  }
129  }
130 
131  BESDEBUG("ppt", "SocketListener::accept() - select() completed without error." << endl);
132 
133  for (Socket_citer i = _socket_list.begin(), e = _socket_list.end(); i != e; i++) {
134  Socket *s_ptr = (*i).second;
135  if (FD_ISSET( s_ptr->getSocketDescriptor(), &read_fd )) {
136  struct sockaddr from;
137  socklen_t len_from = sizeof(from);
138 
139  BESDEBUG("ppt",
140  "SocketListener::accept() - Attempting to accept on "<< s_ptr->getIp() <<":"<<s_ptr->getPort() << endl);
141  int msgsock;
142  while ((msgsock = ::accept(s_ptr->getSocketDescriptor(), &from, &len_from)) < 0) {
143  if (errno == EINTR) {
144  BESDEBUG("ppt2", "SocketListener::accept() - accept() was interrupted" << endl);
145  continue;
146  }
147  else {
148  throw BESInternalError(string("accept: ") + strerror(errno), __FILE__, __LINE__);
149  }
150  }
151 
152  BESDEBUG("ppt", "SocketListener::accept() - END (returning new Socket)" << endl);
153  return s_ptr->newSocket(msgsock, (struct sockaddr *) &from);
154  }
155  }
156 
157  BESDEBUG("ppt", "SocketListener::accept() - END (returning 0)" << endl);
158  return 0;
159 }
160 
167 void SocketListener::dump(ostream &strm) const
168 {
169  strm << BESIndent::LMarg << "SocketListener::dump - (" << (void *) this << ")" << endl;
171  if (_socket_list.size()) {
172  strm << BESIndent::LMarg << "registered sockets:" << endl;
173  Socket_citer i = _socket_list.begin();
174  Socket_citer ie = _socket_list.end();
175  for (; i != ie; i++) {
176  strm << BESIndent::LMarg << "socket: " << (*i).first;
177  Socket *s_ptr = (*i).second;
178  s_ptr->dump(strm);
179  }
180  }
181  else {
182  strm << BESIndent::LMarg << "registered sockets: none" << endl;
183  }
185 }
186 
Definition: Socket.h:42
virtual bool isListening()
Definition: Socket.h:66
exception thrown if inernal error encountered
virtual bool isConnected()
Definition: Socket.h:61
virtual ~SocketListener()
unsigned int getPort()
Definition: Socket.h:82
virtual void dump(ostream &strm) const
dumps information about this object
Definition: Socket.cc:134
static void Indent()
Definition: BESIndent.cc:38
virtual Socket * accept()
Use the select() system call to wait for an incoming connection.
static ostream & LMarg(ostream &strm)
Definition: BESIndent.cc:73
virtual void listen()=0
virtual void dump(ostream &strm) const
dumps information about this object
virtual int getSocketDescriptor()
Definition: Socket.h:78
std::string getIp()
Definition: Socket.h:86
#define BESDEBUG(x, y)
macro used to send debug information to the debug stream
Definition: BESDebug.h:64
static void UnIndent()
Definition: BESIndent.cc:44
virtual void listen(Socket *s)
virtual Socket * newSocket(int socket, struct sockaddr *addr)=0