dodo  0.0.1
A C++ library to create containerized Linux services
tcpserver.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the dodo library (https://github.com/jmspit/dodo).
3  * Copyright (c) 2019 Jan-Marten Spit.
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 /**
19  * @file tcpserver.cpp
20  * Implements the dodo::network::TCPServer class.
21  */
22 
23 #include <network/tcpserver.hpp>
24 #include <common/logger.hpp>
25 #include <common/util.hpp>
26 
27 #include <chrono>
28 #include <thread>
29 #include <cmath>
30 
31 
32 namespace dodo {
33 
34  namespace network {
35 
37  listener_(listener),
38  request_stop_(false),
39  has_stopped_(false),
40  busy_(false) {
41  gettimeofday( &last_active_, NULL );
42  }
43 
44  void TCPServer::run() {
45  has_stopped_ = false;
47  struct timeval now, last_snap;
48  gettimeofday( &last_active_, NULL );
49  gettimeofday( &now, NULL );
50  snapRUsage();
51  gettimeofday( &last_snap, NULL );
52  state_ = ssWait;
53  try {
54  do {
55  if ( listener_.waitForActivity( this ) ) {
56 
57  busy_ = true;
58  gettimeofday( &last_active_, NULL );
59 
60  state_ = ssAwoken;
61 
62  do {
63 
64  sockmap = listener_.popWork();
65 
66  if ( sockmap ) {
67 
68  log_Debug( "TCPServer::run notify wakeup " <<
69  sockmap->socket->debugString() << " state " << sockmap->state );
70 
72 
73  if ( sockmap->state & TCPListener::SockState::New ) {
74  log_Debug( "TCPServer::run ssNew " <<
75  sockmap->socket->debugString() << " state " << sockmap->state );
77  try {
78  bool ok = false;
79  ssize_t received = 0;
80  ssize_t sent = 0;
81  ok = handShake( sockmap->socket, received, sent );
82  listener_.addReceivedSentBytes( received, sent );
84  completion_state |= TCPListener::SockState::New;
85  if ( !ok ) {
86  completion_state |= TCPListener::SockState::Shut;
87  log_Error( "TCPServer::run handshake failure socket " <<
88  sockmap->socket->debugString() );
89  }
90  }
91  catch ( std::exception &e ) {
92  log_Error( "TCPServer::run exception in handshake " << e.what()
93  << " socket " << sockmap->socket->debugString() );
94  }
95  catch ( ... ) {
96  log_Error( "TCPServer::run unhandled exception in handshake " <<
97  sockmap->socket->debugString() );
98  }
99  }
100 
101  if ( sockmap->state & TCPListener::SockState::Read ) {
102  log_Debug( "TCPServer::run ssRead " <<
103  sockmap->socket->debugString() << " state " << sockmap->state );
105  try {
106  bool ok = false;
107  ssize_t received = 0;
108  ssize_t sent = 0;
109 
110  common::SystemError error = sockmap->data->readBuffer( sockmap->socket, received );
111  ok = (error == common::SystemError::ecOK);
112  error = readSocket( *sockmap, sent );
113  ok = ok && ( error == common::SystemError::ecOK || error == common::SystemError::ecEAGAIN );
114  listener_.addReceivedSentBytes( received, sent );
116  completion_state |= TCPListener::SockState::Read;
117  if ( !ok ) {
118  completion_state |= TCPListener::SockState::Shut;
119  if ( error != common::SystemError::ecECONNABORTED ) {
120  log_Error( "TCPServer::run readSocket failure socket " <<
121  sockmap->socket->getFD() << " client " <<
122  sockmap->socket->getPeerAddress().asString(true) );
123  }
124  }
125  }
126  catch ( std::exception &e ) {
127  log_Error( "TCPServer::run exception in ssReadSocket " << e.what()
128  << " socket " << sockmap->socket->getFD() );
129  }
130  catch ( ... ) {
131  log_Error( "TCPServer::run unhandled exception in ssReadSocket socket " <<
132  sockmap->socket->getFD() );
133  }
135  }
136 
137  if ( sockmap->state & TCPListener::SockState::Shut ) {
138  log_Debug( "TCPServer::run ssShut " <<
139  sockmap->socket->debugString() << " state " << sockmap->state );
140  state_ = ssShutdown;
141  try {
142  shutDown( sockmap->socket );
144  completion_state |= TCPListener::SockState::Shut;
145  }
146  catch ( std::exception &e ) {
147  log_Error( "TCPServer::run exception in ssShutdown " << e.what()
148  << " socket " << sockmap->socket->debugString() );
149  }
150  catch ( ... ) {
151  log_Error( "TCPServer::run unhandled exception in ssShutdown " <<
152  sockmap->socket->debugString() );
153  }
154  }
156  listener_.releaseWork( { sockmap->socket, completion_state } );
158  }
159 
160  } while ( sockmap );
161 
162  busy_ = false;
163  state_ = ssWait;
164  }
165  gettimeofday( &now, NULL );
166  if ( common::getSecondDiff( last_snap, now ) > 0.2 ) {
167  snapRUsage();
168  gettimeofday( &last_snap, NULL );
169  }
170 
171  } while ( !request_stop_ );
172  log_Debug( "TCPServer::run stopping" );
173  }
174  catch ( const std::exception &e ) {
175  log_Fatal( "TCPServer::run caught unhandled exception " << e.what() );
176  }
177  catch ( ... ) {
178  log_Fatal( "TCPServer::run caught std::exception " );
179  }
180  has_stopped_ = true;
181  }
182 
184  struct timeval tv;
185  gettimeofday( &tv, 0 );
186  return common::getSecondDiff( last_active_, tv );
187  }
188 
189  }
190 
191 }
dodo::network::TCPServer::ssReadSocket
@ ssReadSocket
The TCPServer is about to invoke readSocket(BaseSocket*).
Definition: tcpserver.hpp:68
dodo::network::TCPServer::TCPServer
TCPServer(TCPListener &listener)
Construct a TCPServer for the TCPListener.
Definition: tcpserver.cpp:36
dodo::network::TCPServer::getIdleSeconds
double getIdleSeconds()
Get the number of secodns the TCPServer has been idle.
Definition: tcpserver.cpp:183
dodo::network::TCPListener::SocketWork::data
TCPConnectionData * data
Data context.
Definition: tcplistener.hpp:277
dodo::network::BaseSocket::getPeerAddress
Address getPeerAddress() const
Get the peer (remote) address for this socket.
Definition: basesocket.cpp:273
dodo::network::TCPServer::ssShutdown
@ ssShutdown
The TCPServer is about to invoke shutdown(BaseSocket*).
Definition: tcpserver.hpp:70
dodo::network::TCPListener::SockState
SockState
BaseSocket lifecycle states.
Definition: tcplistener.hpp:260
dodo::network::TCPServer::handShake
virtual bool handShake(network::BaseSocket *socket, ssize_t &received, ssize_t &sent)=0
Override to perform a protocol handshake.
dodo::network::TCPListener::SockState::None
@ None
Undefined / initial.
dodo::network::TCPServer::request_stop_
bool request_stop_
If true, the TCPServer should stop.
Definition: tcpserver.hpp:174
dodo::network::Address::asString
std::string asString(bool withport=false) const
Return a string representation of this Address.
Definition: address.cpp:107
dodo::network::TCPListener::SocketWork::state
SockState state
State of the socket.
Definition: tcplistener.hpp:275
dodo::network::TCPListener::SockState::New
@ New
New connection, TCPServer::handShake() will be called.
dodo::network::TCPServer::ssAwoken
@ ssAwoken
The TCPServer has woken up from a wait either due to an event or the wait timing out.
Definition: tcpserver.hpp:65
dodo::network::TCPServer::ssShutdownDone
@ ssShutdownDone
ssShutdown completed.
Definition: tcpserver.hpp:71
dodo::network::TCPListener::SockState::Shut
@ Shut
BaseSocket is hung up or in error, TCPServer::shutDown() will be called.
dodo::network::TCPServer::ssHandshakeDone
@ ssHandshakeDone
ssHandshake completed.
Definition: tcpserver.hpp:67
dodo::common::SystemError::ecEAGAIN
@ ecEAGAIN
11 Try again
Definition: systemerror.hpp:71
dodo::network::TCPServer::has_stopped_
bool has_stopped_
If true, the TCPServer has stopped.
Definition: tcpserver.hpp:179
dodo::network::TCPServer::ssHandshake
@ ssHandshake
The TCPServer is about to invoke handShake(BaseSocket*).
Definition: tcpserver.hpp:66
dodo::network::TCPServer::run
virtual void run()
Run the TCPServer thread.
Definition: tcpserver.cpp:44
dodo::network::TCPListener::SocketWork::socket
BaseSocket * socket
Pointer to the socket.
Definition: tcplistener.hpp:273
log_Fatal
#define log_Fatal(what)
Macro to log Fatal.
Definition: logger.hpp:271
dodo::network::TCPListener::releaseWork
void releaseWork(const SocketWork &work)
Called by a TCPServer to signal that the work has been handled and event detection on it can resume.
Definition: tcplistener.cpp:435
dodo
A C++ platform interface to lean Linux services tailored for containerized deployment.
Definition: application.hpp:29
log_Error
#define log_Error(what)
Macro to log Error.
Definition: logger.hpp:277
dodo::network::TCPServer::last_active_
struct timeval last_active_
Time of last request handling.
Definition: tcpserver.hpp:189
dodo::network::TCPListener::waitForActivity
bool waitForActivity(TCPServer *server)
Called by the TCPServer, enters wait state until woken up by either a timeout or a notify by TCPListe...
Definition: tcplistener.cpp:224
dodo::network::TCPServer::ssReleaseWork
@ ssReleaseWork
The TCPServer is releasing the request.
Definition: tcpserver.hpp:72
dodo::network::TCPListener::popWork
SocketWork * popWork()
Pop work.
Definition: tcplistener.cpp:420
dodo::threads::Thread::snapRUsage
void snapRUsage()
Take a snapshot of the thread's resource usage.
Definition: thread.cpp:68
dodo::network::TCPListener::SockState::Read
@ Read
Data is ready to be read, TCPServer::readSocket() will be called.
dodo::network::TCPServer::ssWait
@ ssWait
The TCPServer has entered waiting for activity or wait timeout.
Definition: tcpserver.hpp:64
dodo::network::BaseSocket::getFD
int getFD() const
Return the socket file descriptor.
Definition: basesocket.hpp:121
dodo::network::TCPServer::busy_
bool busy_
If true, the TCPServer is processing a request.
Definition: tcpserver.hpp:184
dodo::common::SystemError::ecOK
@ ecOK
0 Not an error, success
Definition: systemerror.hpp:60
dodo::network::TCPServer::ssReadSocketDone
@ ssReadSocketDone
ssReadSocket completed.
Definition: tcpserver.hpp:69
dodo::common::getSecondDiff
double getSecondDiff(struct timeval &t1, struct timeval &t2)
Return difference in seconds as a double.
Definition: util.hpp:126
dodo::common::DebugObject::debugString
std::string debugString() const
Return the object dump to string.
Definition: exception.cpp:32
dodo::network::TCPConnectionData::readBuffer
common::SystemError readBuffer(BaseSocket *socket, ssize_t &received)
Reads and appends data to read_buffer.
Definition: tcplistener.cpp:38
dodo::network::TCPServer::ssReleaseWorkDone
@ ssReleaseWorkDone
ssReleaseWork completed.
Definition: tcpserver.hpp:73
dodo::network::TCPListener::SocketWork
BaseSocket socket and state pair.
Definition: tcplistener.hpp:271
dodo::network::TCPListener::addReceivedSentBytes
void addReceivedSentBytes(ssize_t r, ssize_t s)
Add received bytes.
Definition: tcplistener.hpp:408
dodo::network::TCPServer::readSocket
virtual common::SystemError readSocket(TCPListener::SocketWork &work, ssize_t &sent)=0
Override to perform a request-response cycle.
dodo::common::SystemError::ecECONNABORTED
@ ecECONNABORTED
103 Software caused connection abort
Definition: systemerror.hpp:163
dodo::network::TCPServer::state_
ServerState state_
State of the TCPServer.
Definition: tcpserver.hpp:194
dodo::common::SystemError
Linux system error primitive to provide a consistent interface to Linux error codes.
Definition: systemerror.hpp:53
logger.hpp
dodo::network::TCPListener
The TCPListener listens, accepts connections and generates socket events to produce TCP work to a poo...
Definition: tcplistener.hpp:126
dodo::network::TCPServer::shutDown
virtual void shutDown(network::BaseSocket *socket)=0
Override to perform a shutdown.
dodo::network::TCPServer::listener_
TCPListener & listener_
The TCPListener the TCPServer is working for.
Definition: tcpserver.hpp:162
log_Debug
#define log_Debug(what)
Macro to log Debug.
Definition: logger.hpp:302
tcpserver.hpp
util.hpp