Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

foreach_conns_except because i can't make boost lambda behave..

  • Loading branch information...
commit 162ed96d34d5a635ec70722a7e22aa4a7ae43682 1 parent 74ed16b
Richard Jones authored
31 app/main.cpp
@@ -4,7 +4,8 @@
4 4 #include <boost/asio.hpp>
5 5 #include <boost/thread/thread.hpp>
6 6 #include <boost/algorithm/string.hpp>
7   -
  7 +#include "boost/lambda/lambda.hpp"
  8 +#include <boost/lambda/if.hpp>
8 9
9 10 #include "libf2f/router.h"
10 11 #include "libf2f/protocol.h"
@@ -12,6 +13,7 @@
12 13 using namespace std;
13 14 using namespace libf2f;
14 15
  16 +
15 17 void iorun( boost::asio::io_service * ios )
16 18 {
17 19 ios->run();
@@ -31,13 +33,14 @@ int main(int argc, char **argv)
31 33 Protocol p;
32 34 short port = atoi(argv[1]);
33 35 cout << "Listening on port " << port << endl;
34   - boost::asio::ip::tcp::acceptor acc(
35   - ios,
36   - boost::asio::ip::tcp::endpoint(
37   - boost::asio::ip::tcp::v4(),
38   - port)
  36 + boost::shared_ptr<boost::asio::ip::tcp::acceptor> accp(
  37 + new boost::asio::ip::tcp::acceptor
  38 + (ios, boost::asio::ip::tcp::endpoint(
  39 + boost::asio::ip::tcp::v4(),
  40 + port)
  41 + )
39 42 );
40   - Router r(acc, &p);
  43 + Router r(accp, &p);
41 44
42 45 boost::thread t( boost::bind(&iorun, &ios) );
43 46
@@ -66,12 +69,20 @@ int main(int argc, char **argv)
66 69
67 70 if(parts[0] == "pingall")
68 71 {
69   - message_ptr ping = PingMessage::factory();
70   - r.foreach_conns( boost::bind(&Connection::async_write, _1, ping) );
  72 + message_ptr ping = message_ptr(new PingMessage());
  73 + r.send_all(ping);
  74 + }
  75 +
  76 + if(parts[0] == "query" && parts.size() == 2)
  77 + {
  78 + message_ptr search = message_ptr(new GeneralMessage(QUERY, parts[1]) );
  79 + r.send_all(search);
71 80 }
  81 +
  82 + if(parts[0] == "quit") break;
72 83 }
73 84
74   - ios.stop();
  85 + r.stop();
75 86 t.join();
76 87 return 0;
77 88 }
12 include/libf2f/connection.h
@@ -4,6 +4,7 @@
4 4 #include <boost/asio.hpp>
5 5 #include <boost/bind.hpp>
6 6 #include <boost/shared_ptr.hpp>
  7 +#include <boost/weak_ptr.hpp>
7 8 #include <boost/enable_shared_from_this.hpp>
8 9 #include <boost/tuple/tuple.hpp>
9 10 #include <boost/thread/thread.hpp>
@@ -19,6 +20,7 @@ namespace libf2f {
19 20
20 21 class Connection;
21 22 typedef boost::shared_ptr<Connection> connection_ptr;
  23 +typedef boost::weak_ptr<Connection> connection_ptr_weak;
22 24
23 25 /// This class represents a Connection to one other libf2f user.
24 26 /// it knows how to marshal objects to and from the wire protocol
@@ -60,7 +62,8 @@ class Connection
60 62
61 63 size_t drain_writeq( std::deque< message_ptr > & out );
62 64
63   - void set_message_received_cb( boost::function< void(message_ptr, connection_ptr) > cb );
  65 + void push_message_received_cb( boost::function< void(message_ptr, connection_ptr) > cb );
  66 + void pop_message_received_cb();
64 67
65 68 std::string str() const;
66 69
@@ -74,13 +77,16 @@ class Connection
74 77
75 78 /// Stateful stuff the protocol handler/servent will set:
76 79 std::string m_username; // username of user at end of Connection
77   - bool m_authed;
  80 + //bool m_authed;
78 81 bool m_sending;
79 82
  83 + std::vector< boost::function<void(message_ptr, connection_ptr)> > m_message_received_cbs;
  84 + boost::mutex m_message_received_cb_mutex; // protects the above
80 85
81   - boost::function< void(message_ptr, connection_ptr) > m_message_received_cb;
82 86 boost::function< void(connection_ptr) > m_fin_cb; // call when we die.
83 87
  88 + bool m_shuttingdown;
  89 +
84 90 };
85 91
86 92 } //ns
52 include/libf2f/message.h
@@ -75,7 +75,6 @@ class Message
75 75 : m_payload(0)
76 76 {
77 77 m_header = header;
78   - m_guid = std::string(header.guid, 36);
79 78 //std::cout << "CTOR Msg(" << m_guid << ")" << std::endl;
80 79 }
81 80
@@ -101,10 +100,23 @@ class Message
101 100 virtual const short ttl() const { return m_header.ttl; }
102 101 virtual const short hops() const { return m_header.hops; }
103 102 virtual const boost::uint32_t length() const { return ntohl(m_header.length); }
104   - virtual const std::string& guid() const { return m_guid; }
  103 + virtual const std::string& guid() const
  104 + {
  105 + if( m_guid.empty() )
  106 + {
  107 + m_guid = std::string(m_header.guid, 36);
  108 + }
  109 + return m_guid;
  110 + }
105 111 // payload
106 112 virtual const char * payload() const { return m_payload; }
107 113 virtual char * payload() { return m_payload; }
  114 + /// sucks to have to do this really, jsonspirit needs a string or stream:
  115 + virtual std::string payload_str() const
  116 + {
  117 + std::string s(m_payload, length());
  118 + return s;
  119 + }
108 120
109 121 virtual size_t malloc_payload()
110 122 {
@@ -132,16 +144,18 @@ class Message
132 144 return buffers;
133 145 }
134 146
135   -private:
  147 +protected:
136 148 message_header m_header;
137   - std::string m_guid;
  149 + mutable std::string m_guid;
138 150 char * m_payload;
139 151 };
140 152
  153 +
  154 +
141 155 class PongMessage : public Message
142 156 {
143 157 public:
144   - static boost::shared_ptr<Message> factory()
  158 + PongMessage()
145 159 {
146 160 message_header h;
147 161 memcpy( &h.guid, std::string(GENUUID).data(), 36 );
@@ -150,26 +164,47 @@ class PongMessage : public Message
150 164 h.ttl = 1;
151 165 h.hops = 0;
152 166 h.length = 0;
153   - return boost::shared_ptr<Message>(new Message( h ));
  167 + m_header = h;
  168 + m_payload = 0;
154 169 }
155 170 };
156 171
157 172 class PingMessage : public Message
158 173 {
159 174 public:
160   - static boost::shared_ptr<Message> factory()
  175 + PingMessage()
161 176 {
162 177 message_header h;
163 178 memcpy( &h.guid, std::string(GENUUID).data(), 36 );
  179 + //h.guid = GENUUID;
164 180 h.type = PING;
165 181 h.ttl = 1;
166 182 h.hops = 0;
167 183 h.length = 0;
168   - return boost::shared_ptr<Message>(new Message( h ));
  184 + m_header = h;
  185 + m_payload = 0;
169 186 }
170 187 };
171 188
  189 +class GeneralMessage : public Message
  190 +{
  191 +public:
  192 + GeneralMessage(const char msgtype, const std::string& body)
  193 + {
  194 + message_header h;
  195 + memcpy( &h.guid, std::string(GENUUID).data(), 36 );
  196 + //h.guid = GENUUID;
  197 + h.type = msgtype;
  198 + h.ttl = 1;
  199 + h.hops = 0;
  200 + h.length = htonl( body.length() );
  201 + m_header = h;
  202 + malloc_payload();
  203 + memcpy( m_payload, body.data(), body.length() );
  204 + }
  205 +};
172 206
  207 +/*
173 208
174 209
175 210 /// File transfer/streaming msgs are special in that they have an additional header describing the payload (just the sid)
@@ -195,6 +230,7 @@ class DataMessage : public Message
195 230 private:
196 231 mutable std::string m_sid;
197 232 };
  233 +*/
198 234
199 235 }
200 236 #endif
7 include/libf2f/protocol.h
@@ -10,6 +10,8 @@ class Protocol
10 10 {
11 11 public:
12 12 Protocol();
  13 +
  14 + virtual ~Protocol(){}
13 15
14 16 virtual void set_router(Router * r) { m_router = r; }
15 17
@@ -19,10 +21,13 @@ class Protocol
19 21 /// called when we opened a socket to a remote servent
20 22 virtual void new_outgoing_connection( connection_ptr conn );
21 23
  24 + /// called on a disconnection, for whatever reason
  25 + virtual void connection_terminated( connection_ptr conn ){}
  26 +
22 27 /// we received a msg from this connection
23 28 virtual void message_received( message_ptr msgp, connection_ptr conn );
24 29
25   -private:
  30 +protected:
26 31 Router * m_router;
27 32 };
28 33
23 include/libf2f/router.h
@@ -5,6 +5,8 @@
5 5 #include <boost/shared_ptr.hpp>
6 6 #include <boost/bind.hpp>
7 7 #include <boost/lexical_cast.hpp>
  8 +#include "boost/lambda/lambda.hpp"
  9 +#include <boost/lambda/if.hpp>
8 10 #include <iostream>
9 11 #include <vector>
10 12
@@ -17,8 +19,6 @@ class Protocol;
17 19
18 20
19 21 /// aka servent - responsible for managing connections
20   -/// it will authenticate connections before creating a Connection
21   -/// object and starting to send/recv data using flow control mechanism.
22 22 class Router
23 23 {
24 24 public:
@@ -28,8 +28,10 @@ class Router
28 28 /// acceptor(io_service,
29 29 /// boost::asio::ip::tcp::endpoint(
30 30 /// boost::asio::ip::tcp::v4(), port) )
31   - Router( boost::asio::ip::tcp::acceptor & acc, Protocol * p );
  31 + Router( boost::shared_ptr<boost::asio::ip::tcp::acceptor> accp, Protocol * p );
32 32
  33 + /// calls io_service::stop on acceptor.
  34 + void stop();
33 35
34 36 /// Handle completion of a accept operation.
35 37 void handle_accept(const boost::system::error_code& e, connection_ptr conn);
@@ -42,24 +44,35 @@ class Router
42 44 boost::asio::ip::tcp::endpoint &endpoint,
43 45 connection_ptr conn);
44 46
  47 + /// connection terminated for any reason
  48 + void connection_terminated( connection_ptr conn );
  49 +
45 50 /// Default message recvd callback
46 51 void message_received( message_ptr msgp, connection_ptr conn );
47 52
48 53 /// apply function to all registered connections
49 54 void foreach_conns( boost::function<void(connection_ptr)> );
50 55
  56 + /// apply function to all registered connections *except* conn
  57 + void foreach_conns_except( boost::function<void(connection_ptr)> fun, connection_ptr conn );
  58 +
  59 + /// send msg to all registered connections
  60 + void send_all( message_ptr msgp );
  61 +
  62 + std::string connections_str();
51 63 private:
52 64 /// Router keeps track of connections:
53 65 void register_connection( connection_ptr conn );
54 66 void unregister_connection( connection_ptr conn );
55   - void connections_str();
  67 +
56 68
57 69 /// all connections:
58 70 std::vector< connection_ptr > m_connections;
59 71 boost::mutex m_connections_mutex; // protects connections
60 72
61 73 /// The acceptor object used to accept incoming socket connections.
62   - boost::asio::ip::tcp::acceptor & m_acceptor;
  74 + boost::shared_ptr<boost::asio::ip::tcp::acceptor> m_acceptor;
  75 +
63 76 /// protocol implementation
64 77 Protocol * m_protocol;
65 78 /// thread that enforces flow-control and sends outgoing msgs
34 src/connection.cpp
@@ -8,11 +8,12 @@ using namespace std;
8 8 Connection::Connection( boost::asio::io_service& io_service,
9 9 boost::function< void(message_ptr, connection_ptr) > msg_cb,
10 10 boost::function< void(connection_ptr) > fin_cb )
11   - : m_socket(io_service), m_authed(false), m_sending(false),
12   - m_message_received_cb(msg_cb),
13   - m_fin_cb(fin_cb)
  11 + : m_socket(io_service), m_sending(false),
  12 + m_fin_cb(fin_cb),
  13 + m_shuttingdown(false)
14 14 {
15 15 std::cout << "CTOR connection" << std::endl;
  16 + push_message_received_cb( msg_cb );
16 17 max_writeq_size = 20*1024; // 20kb
17 18 }
18 19
@@ -30,6 +31,7 @@ Connection::close()
30 31 void
31 32 Connection::fin()
32 33 {
  34 + m_shuttingdown = true;
33 35 std::cout << "FIN connection " << str() << std::endl;
34 36 m_fin_cb( shared_from_this() );
35 37 close();
@@ -60,6 +62,7 @@ Connection::async_write(message_ptr msg)
60 62 void
61 63 Connection::async_read()
62 64 {
  65 + if( m_shuttingdown ) return;
63 66 message_ptr msgp(new Message());
64 67 // Read exactly the number of bytes in a header
65 68 boost::asio::async_read(m_socket,
@@ -76,6 +79,7 @@ void
76 79 Connection::handle_read_header(const boost::system::error_code& e, message_ptr msgp)
77 80 {
78 81 //cout << "handle_read_header" << endl;
  82 + if( m_shuttingdown ) return;
79 83 if (e)
80 84 {
81 85 std::cerr << "err" << std::endl;
@@ -107,23 +111,37 @@ Connection::handle_read_header(const boost::system::error_code& e, message_ptr m
107 111 void
108 112 Connection::handle_read_data(const boost::system::error_code& e, message_ptr msgp)
109 113 {
  114 + if( m_shuttingdown ) return;
110 115 if (e)
111 116 {
112 117 std::cerr << "errrrrrr" << std::endl;
113 118 fin();
114 119 return;
115 120 }
116   - // inform router that we received a new message
117   - m_message_received_cb( msgp, shared_from_this() );
  121 + // report that we received a new message
  122 + {
  123 + boost::mutex::scoped_lock lk(m_message_received_cb_mutex);
  124 + m_message_received_cbs.back()( msgp, shared_from_this() );
  125 + }
118 126 // setup recv for next message:
119 127 async_read();
120 128 }
121 129
  130 +
  131 +void
  132 +Connection::push_message_received_cb( boost::function< void(message_ptr, connection_ptr) > cb )
  133 +{
  134 + boost::mutex::scoped_lock lk(m_message_received_cb_mutex);
  135 + m_message_received_cbs.push_back(cb);
  136 +}
  137 +
122 138 void
123   -Connection::set_message_received_cb( boost::function< void(message_ptr, connection_ptr) > cb )
  139 +Connection::pop_message_received_cb()
124 140 {
125   - m_message_received_cb = cb;
  141 + boost::mutex::scoped_lock lk(m_message_received_cb_mutex);
  142 + m_message_received_cbs.pop_back();
126 143 }
  144 +
127 145 /// unused atm.. todo flow control?
128 146 size_t
129 147 Connection::drain_writeq( std::deque< message_ptr > & out )
@@ -145,6 +163,8 @@ Connection::str() const
145 163 std::ostringstream os;
146 164 os << "[Connection:"
147 165 << m_socket.remote_endpoint().address().to_string()
  166 + << ":"
  167 + << m_socket.remote_endpoint().port()
148 168 << "]";
149 169 return os.str();
150 170 }
4 src/protocol.cpp
@@ -13,7 +13,7 @@ bool
13 13 Protocol::new_incoming_connection( connection_ptr conn )
14 14 {
15 15 cout << "Protocol::new_incoming_connection " << conn->str() << endl;
16   - conn->async_write( PingMessage::factory() );
  16 + conn->async_write( message_ptr(new PingMessage()) );
17 17 // first thing to expect is an ident msg, so set the msg handler to one
18 18 // that expects it, and kills the connection otherwise:
19 19
@@ -24,7 +24,7 @@ void
24 24 Protocol::new_outgoing_connection( connection_ptr conn )
25 25 {
26 26 cout << "Protocol::new_outgoing_connection " << conn->str() << endl;
27   - conn->async_write( PingMessage::factory() );
  27 + conn->async_write( message_ptr(new PingMessage()) );
28 28 }
29 29
30 30 void
62 src/router.cpp
@@ -7,27 +7,41 @@
7 7 #include <boost/foreach.hpp>
8 8
9 9 // How we typically prep a new connection object:
10   -#define NEWCONN new Connection(m_acceptor.io_service(), \
  10 +#define NEWCONN new Connection(m_acceptor->io_service(), \
11 11 boost::bind( &Router::message_received, this, _1, _2 ), \
12   - boost::bind( &Router::unregister_connection, this, _1) )
  12 + boost::bind( &Router::connection_terminated, this, _1) )
13 13
14 14 namespace libf2f {
15 15
16 16 using namespace std;
17 17
18   -Router::Router( boost::asio::ip::tcp::acceptor& acc, Protocol * p )
19   - : m_acceptor( acc ), m_protocol( p ), seen_connections(0)
  18 +Router::Router( boost::shared_ptr<boost::asio::ip::tcp::acceptor> accp,
  19 + Protocol * p )
  20 + : m_acceptor( accp ), m_protocol( p ), seen_connections(0)
20 21 {
21 22 p->set_router( this );
22 23 // Start an accept operation for a new connection.
23 24 connection_ptr new_conn(NEWCONN);
24 25
25   - m_acceptor.async_accept(new_conn->socket(),
  26 + m_acceptor->async_accept(new_conn->socket(),
26 27 boost::bind(&Router::handle_accept, this,
27 28 boost::asio::placeholders::error, new_conn));
28 29
29 30 }
30 31
  32 +void
  33 +Router::stop()
  34 +{
  35 + m_acceptor->get_io_service().stop();
  36 +}
  37 +
  38 +void
  39 +Router::connection_terminated( connection_ptr conn )
  40 +{
  41 + unregister_connection( conn );
  42 + m_protocol->connection_terminated( conn );
  43 +}
  44 +
31 45 /// Handle completion of a accept operation.
32 46 void
33 47 Router::handle_accept(const boost::system::error_code& e, connection_ptr conn)
@@ -54,7 +68,7 @@ Router::handle_accept(const boost::system::error_code& e, connection_ptr conn)
54 68 // Start an accept operation for a new connection.
55 69 connection_ptr new_conn(NEWCONN);
56 70
57   - m_acceptor.async_accept(new_conn->socket(),
  71 + m_acceptor->async_accept(new_conn->socket(),
58 72 boost::bind(&Router::handle_accept, this,
59 73 boost::asio::placeholders::error, new_conn));
60 74 }
@@ -75,7 +89,7 @@ Router::register_connection( connection_ptr conn )
75 89 }
76 90 }
77 91 m_connections.push_back( conn );
78   - connections_str();
  92 + cout << connections_str() << endl;
79 93 }
80 94
81 95 void
@@ -91,18 +105,20 @@ Router::unregister_connection( connection_ptr conn )
91 105 cout << "Router::unregistered " << conn->str() << endl;
92 106 }
93 107 }
94   - connections_str();
  108 + cout << connections_str() << endl;
95 109 }
96 110
97   -void
  111 +string
98 112 Router::connections_str()
99 113 {
100   - cout << "<connections>" << endl;
  114 + ostringstream os;
  115 + os << "<connections>" << endl;
101 116 BOOST_FOREACH( connection_ptr conn, m_connections )
102 117 {
103   - cout << conn->str() << endl;
  118 + os << conn->str() << endl;
104 119 }
105   - cout << "</connections>" << endl;
  120 + os << "</connections>" << endl;
  121 + return os.str();
106 122 }
107 123
108 124 /// this is the default msg recvd callback passed to new connections:
@@ -126,7 +142,7 @@ Router::message_received( message_ptr msgp, connection_ptr conn )
126 142 if( msgp->type() == PING )
127 143 {
128 144 cout << "got PING, responding.." << endl;
129   - conn->async_write( PongMessage::factory() );
  145 + conn->async_write( message_ptr(new PongMessage()) );
130 146 return;
131 147 }
132 148
@@ -183,4 +199,24 @@ Router::foreach_conns( boost::function<void(connection_ptr)> fun )
183 199 }
184 200
185 201
  202 +
  203 +void
  204 +Router::foreach_conns_except( boost::function<void(connection_ptr)> fun, connection_ptr conn )
  205 +{
  206 + boost::mutex::scoped_lock lk(m_connections_mutex);
  207 + BOOST_FOREACH( connection_ptr c, m_connections )
  208 + {
  209 + if( c == conn ) continue;
  210 + fun( c );
  211 + }
  212 +}
  213 +
  214 +void
  215 +Router::send_all( message_ptr msgp )
  216 +{
  217 + foreach_conns( boost::bind(&Connection::async_write, _1, msgp) );
  218 +}
  219 +
  220 +
  221 +
186 222 } //ns

0 comments on commit 162ed96

Please sign in to comment.
Something went wrong with that request. Please try again.