aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDeterminant <tederminant@gmail.com>2018-07-25 19:48:09 -0400
committerDeterminant <tederminant@gmail.com>2018-07-25 19:48:09 -0400
commit9cc990a009a4702edf805dc5818bf4cbfffcf140 (patch)
treed327f62805d7126300568300e49b7b009b1495bd
parent1b006d5164c605ef44652b4442309983bfc0d24a (diff)
add MsgNetwork benchmark
-rw-r--r--README.rst25
-rw-r--r--include/salticidae/msg.h31
-rw-r--r--include/salticidae/network.h3
-rw-r--r--test/.gitignore1
-rw-r--r--test/CMakeLists.txt3
-rw-r--r--test/bench_network.cpp158
6 files changed, 217 insertions, 4 deletions
diff --git a/README.rst b/README.rst
index 827341d..5cf9366 100644
--- a/README.rst
+++ b/README.rst
@@ -23,6 +23,31 @@ Features
- Utilities. The library also provides with some useful gadgets, such as
command-line parser, libevent abstraction, etc.
+Functionalities
+---------------
+
+- `ConnPool`: A byte level connection pool implementation, the `ConnPool::Conn`
+ (or `ConnPool::conn_t`) objects represent a connection to which one can
+ send/receive a binary stream of data asynchronously.
+
+- `MsgNetwork<OpcodeType>`: A message level network pool implementation, the
+ `MsgNetwork::Conn` (or `MsgNetwork::cont_t`) objects represent a channel to
+ which one can send/receive a predefined message. Message handler functions
+ are registered by `reg_handler()` and invoked upon receiving a new message.
+ `OpcodeType` is the type used for identifying message types. A vald message
+ type must have `opcode` value as its static member and `serialized` as a
+ member typed `DataStream` which contains the serialized data of the message.
+
+- `PeerNetwork<OpcodeType>`: A simple P2P network pool implementation. It will
+ ensure exactly one underlying bi-directional connection is established per
+ added peer, and retry the connection when it is broken. Ping-pong messages
+ are utilized to test the connectivity at the application layer.
+
+- `ClientNetwork<OpcodeType>`: A simple client-server network pool
+ implementation. A server who initialy calls `listen()` will accept the
+ incoming client messages, while a client simply calls `connect()` to connect
+ to a known server.
+
Dependencies
------------
diff --git a/include/salticidae/msg.h b/include/salticidae/msg.h
index 3a1eebf..bc1a633 100644
--- a/include/salticidae/msg.h
+++ b/include/salticidae/msg.h
@@ -48,7 +48,9 @@ class MsgBase {
uint32_t magic;
opcode_t opcode;
uint32_t length;
+#ifndef SALTICIDAE_NOCHECKSUM
uint32_t checksum;
+#endif
mutable bytearray_t payload;
mutable bool no_payload;
@@ -69,7 +71,9 @@ class MsgBase {
magic(other.magic),
opcode(other.opcode),
length(other.length),
+#ifndef SALTICIDAE_NOCHECKSUM
checksum(other.checksum),
+#endif
payload(other.payload),
no_payload(other.no_payload) {}
@@ -77,7 +81,9 @@ class MsgBase {
magic(other.magic),
opcode(std::move(other.opcode)),
length(other.length),
+#ifndef SALTICIDAE_NOCHECKSUM
checksum(other.checksum),
+#endif
payload(std::move(other.payload)),
no_payload(other.no_payload) {}
@@ -85,24 +91,33 @@ class MsgBase {
uint32_t _magic;
opcode_t _opcode;
uint32_t _length;
+#ifndef SALTICIDAE_NOCHECKSUM
uint32_t _checksum;
+#endif
DataStream s(raw_header, raw_header + MsgBase::header_size);
s >> _magic
>> _opcode
>> _length
- >> _checksum;
+#ifndef SALTICIDAE_NOCHECKSUM
+ >> _checksum
+#endif
+ ;
magic = letoh(_magic);
opcode = _opcode;
length = letoh(_length);
+#ifndef SALTICIDAE_NOCHECKSUM
checksum = letoh(_checksum);
+#endif
}
void swap(MsgBase &other) {
std::swap(magic, other.magic);
std::swap(opcode, other.opcode);
std::swap(length, other.length);
+#ifndef SALTICIDAE_NOCHECKSUM
std::swap(checksum, other.checksum);
+#endif
std::swap(payload, other.payload);
std::swap(no_payload, other.no_payload);
}
@@ -154,7 +169,9 @@ class MsgBase {
no_payload = false;
#endif
length = payload.size();
+#ifndef SALTICIDAE_NOCHECKSUM
checksum = get_checksum();
+#endif
}
operator std::string() const {
@@ -163,11 +180,14 @@ class MsgBase {
<< "magic=" << get_hex(magic) << " "
<< "opcode=" << get_hex(opcode) << " "
<< "length=" << std::to_string(length) << " "
+#ifndef SALTICIDAE_NOCHECKSUM
<< "checksum=" << get_hex(checksum) << " "
+#endif
<< "payload=" << get_hex(payload) << ">";
return std::move(s);
}
+#ifndef SALTICIDAE_NOCHECKSUM
uint32_t get_checksum() const {
static class SHA256 sha256;
uint32_t res;
@@ -189,13 +209,16 @@ class MsgBase {
bool verify_checksum() const {
return checksum == get_checksum();
}
+#endif
bytearray_t serialize() const {
DataStream s;
s << htole(magic)
<< opcode
<< htole(length)
+#ifndef SALTICIDAE_NOCHECKSUM
<< htole(checksum)
+#endif
<< payload;
return std::move(s);
}
@@ -226,8 +249,10 @@ const size_t MsgBase<OpcodeType>::header_size =
sizeof(MsgBase<OpcodeType>::magic) +
sizeof(MsgBase<OpcodeType>::opcode) +
sizeof(MsgBase<OpcodeType>::length) +
- sizeof(MsgBase<OpcodeType>::checksum);
-
+#ifndef SALTICIDAE_NOCHECKSUM
+ sizeof(MsgBase<OpcodeType>::checksum) +
+#endif
+ 0;
}
#endif
diff --git a/include/salticidae/network.h b/include/salticidae/network.h
index 6556d22..2bc445c 100644
--- a/include/salticidae/network.h
+++ b/include/salticidae/network.h
@@ -210,7 +210,6 @@ class ClientNetwork: public MsgNetwork<OpcodeType> {
template<typename MsgType>
void send_msg(const MsgType &msg, const NetAddr &addr);
- conn_t connect(const NetAddr &addr) = delete;
};
class PeerNetworkError: public SalticidaeError {
@@ -387,11 +386,13 @@ void MsgNetwork<OpcodeType>::Conn::on_read() {
bytearray_t data = recv_buffer.pop(len);
msg.set_payload(std::move(data));
msg_state = Conn::HEADER;
+#ifndef SALTICIDAE_NOCHECKSUM
if (!msg.verify_checksum())
{
SALTICIDAE_LOG_WARN("checksums do not match, dropping the message");
return;
}
+#endif
auto it = mn->handler_map.find(msg.get_opcode());
if (it == mn->handler_map.end())
SALTICIDAE_LOG_WARN("unknown opcode: %s",
diff --git a/test/.gitignore b/test/.gitignore
index b514e8d..bc79950 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,4 +1,5 @@
test_msg
test_stream
test_network
+bench_network
Makefile
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index f84384d..58d204f 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -28,3 +28,6 @@ target_link_libraries(test_stream salticidae_static)
add_executable(test_network test_network.cpp)
target_link_libraries(test_network salticidae_static)
+
+add_executable(bench_network bench_network.cpp)
+target_link_libraries(bench_network salticidae_static pthread)
diff --git a/test/bench_network.cpp b/test/bench_network.cpp
new file mode 100644
index 0000000..0276629
--- /dev/null
+++ b/test/bench_network.cpp
@@ -0,0 +1,158 @@
+/**
+ * Copyright (c) 2018 Cornell University.
+ *
+ * Author: Ted Yin <tederminant@gmail.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <cstdio>
+#include <string>
+#include <functional>
+#include <thread>
+
+/* disable SHA256 checksum */
+#define SALTICIDAE_NOCHECKSUM
+
+#include "salticidae/msg.h"
+#include "salticidae/event.h"
+#include "salticidae/network.h"
+#include "salticidae/stream.h"
+
+using salticidae::NetAddr;
+using salticidae::DataStream;
+using salticidae::MsgNetwork;
+using salticidae::htole;
+using salticidae::letoh;
+using salticidae::bytearray_t;
+using salticidae::Event;
+using std::placeholders::_1;
+using std::placeholders::_2;
+using opcode_t = uint8_t;
+
+struct MsgBytes {
+ static const opcode_t opcode = 0x0;
+ DataStream serialized;
+ bytearray_t bytes;
+ MsgBytes(size_t size) {
+ bytes.resize(size);
+ serialized << htole((uint32_t)size) << bytes;
+ }
+ MsgBytes(DataStream &&s) {
+ uint32_t len;
+ s >> len;
+ len = letoh(len);
+ auto base = s.get_data_inplace(len);
+ bytes = bytearray_t(base, base + len);
+ }
+};
+
+const opcode_t MsgBytes::opcode;
+
+using MsgNetworkByteOp = MsgNetwork<opcode_t>;
+
+struct MyNet: public MsgNetworkByteOp {
+ const std::string name;
+ const NetAddr peer;
+ Event ev_period_send;
+ Event ev_period_stat;
+ size_t nrecv;
+
+ MyNet(const salticidae::EventContext &ec,
+ const std::string name,
+ const NetAddr &peer,
+ double stat_timeout = -1):
+ MsgNetwork<opcode_t>(ec, 10, 1.0, 4096),
+ name(name),
+ peer(peer),
+ ev_period_stat(ec, -1, 0, [this, stat_timeout](int, short) {
+ printf("%.2f mps\n", nrecv / (double)stat_timeout);
+ nrecv = 0;
+ ev_period_stat.add_with_timeout(stat_timeout);
+ }),
+ nrecv(0) {
+ /* message handler could be a bound method */
+ reg_handler(salticidae::handler_bind(
+ &MyNet::on_receive_bytes, this, _1, _2));
+ if (stat_timeout > 0)
+ ev_period_stat.add_with_timeout(0);
+ }
+
+ struct Conn: public MsgNetworkByteOp::Conn {
+ MyNet *get_net() { return static_cast<MyNet *>(get_pool()); }
+ salticidae::RcObj<Conn> self() {
+ return salticidae::static_pointer_cast<Conn>(
+ MsgNetworkByteOp::Conn::self());
+ }
+
+ void on_setup() override {
+ auto net = get_net();
+ if (get_mode() == ACTIVE)
+ {
+ printf("[%s] Connected, sending hello.\n",
+ net->name.c_str());
+ /* send the first message through this connection */
+ net->ev_period_send = Event(net->ec, -1, 0,
+ [net, conn = self()](int, short) {
+ net->send_msg(MsgBytes(256), conn);
+ net->ev_period_send.add_with_timeout(0);
+ });
+ net->ev_period_send.add_with_timeout(0);
+
+ }
+ else
+ printf("[%s] Passively connected, waiting for greetings.\n",
+ net->name.c_str());
+ }
+ void on_teardown() override {
+ auto net = get_net();
+ printf("[%s] Disconnected, retrying.\n", net->name.c_str());
+ /* try to reconnect to the same address */
+ net->connect(get_addr());
+ }
+ };
+ using conn_t = salticidae::RcObj<Conn>;
+
+ salticidae::ConnPool::Conn *create_conn() override {
+ return new Conn();
+ }
+
+ void on_receive_bytes(MsgBytes &&msg, conn_t conn) {
+ nrecv++;
+ }
+};
+
+salticidae::EventContext ec;
+NetAddr alice_addr("127.0.0.1:1234");
+NetAddr bob_addr("127.0.0.1:1235");
+
+int main() {
+ /* test two nodes */
+ MyNet alice(ec, "Alice", bob_addr, 10);
+ alice.listen(alice_addr);
+ std::thread bob_thread([]() {
+ salticidae::EventContext ec;
+ MyNet bob(ec, "Bob", alice_addr);
+ bob.connect(alice_addr);
+ ec.dispatch();
+ });
+
+ ec.dispatch();
+ return 0;
+}