#include <gtest/gtest.h>
#include <iostream>
#include <system_error>
#include <thread>
#include <future>
#include <sstream>
#include <chrono>
#include <condition_variable>
#include <mutex>
using std::cout;
using std::endl;
using std::string;
using std::unique_lock;
using std::lock_guard;
using std::mutex;
using std::exception;
using std::condition_variable;
using std::async;
using std::stringstream;
using std::error_code;
using std::to_string;
static int find_interface(int flag, InetAddr* ad = nullptr)
{
{
for (auto& j : i.addrs())
{
if ((j.test_flags(flag)))
{
if (ad)
{
*ad = j;
}
return i.index();
}
}
}
return 0;
}
struct inet_networking : public testing::Test
{
std::string snd, got, testgot;
InetAddr server_addr, client_addr, servfrom;
InetTcpSock tcp_serv_sock, tcp_client_sock;
InetUdpSock udp_serv_sock, udp_client_sock;
inet_networking() : snd("this is a test line\nthis is the second\nanother\n\nlast one")
{
Logger log;
log.id("test");
}
virtual ~inet_networking()
{
}
void reset()
{
tcp_serv_sock.reset();
udp_serv_sock.reset();
tcp_client_sock.reset();
udp_client_sock.reset();
}
void udp_readwrite_client()
{
auto fut = async(udp_readwrite_server, std::ref(startev), std::ref(udp_serv_sock), std::ref(server_addr), std::ref(servfrom), std::ref(quitev));
log << "started" << endl;
udp_writeread_test(udp_client_sock, client_addr);
int res = fut.get();
log << "return code: " << res << endl;
ASSERT_EQ(res, 0);
};
void tcp_iostream_client()
{
auto fut = async(tcp_iostream_server, std::ref(startev), std::ref(tcp_serv_sock), std::ref(server_addr), std::ref(servfrom));
log << "connect" << endl;
tcp_client_sock.connect(server_addr);
log << "connected to addr=" << server_addr.str() << endl;
tcp_stream_test(tcp_client_sock);
tcp_client_sock.close();
int res = fut.get();
log << "return code: " << res << endl;
ASSERT_EQ(res, 0);
}
void tcp_readwrite_client()
{
auto fut = async(tcp_readwrite_server, std::ref(startev), std::ref(tcp_serv_sock), std::ref(server_addr), std::ref(servfrom));
log << "read start event" << endl;
log << "connect" << endl;
tcp_client_sock.connect(server_addr);
log << "connected to addr=" << server_addr.str() << endl;
tcp_writeread_test(tcp_client_sock);
tcp_client_sock.close();
int res = fut.get();
log << "return code: " << res << endl;
ASSERT_EQ(res, 0);
}
void tcp_stream_test(InetTcpSock& sock)
{
Logger log;
log.add_cout();
log.id("inet tcp stream client");
IoStream stream(sock, sock);
stringstream testst(snd);
log << "sending " << snd.size() << endl;
ASSERT_TRUE(stream << snd << endl);
stream.flush();
while (std::getline(testst, testgot))
{
ASSERT_TRUE(std::getline(stream, got));
log << "got reply " << got.size() << endl;
ASSERT_EQ(got, testgot);
}
ASSERT_TRUE(stream << snd << endl);
stream.flush();
stringstream testst2(snd);
while (testst2 >> testgot)
{
ASSERT_TRUE(stream >> got);
log << "got reply " << got.size() << endl;
ASSERT_EQ(got, testgot);
}
log << "end" << endl;
}
void tcp_writeread_test(InetTcpSock& sock)
{
Logger log;
log.add_cout();
log.id("inet tcp readwrite client");
log << "sending " << snd.size() << endl;
ASSERT_EQ(
sock.send(snd.data(), snd.size()), snd.size());
char buf[snd.size()];
size_t sz = 0, got;
do
{
got =
sock.recv(buf+sz, snd.size()-sz);
log << "got reply " << got << endl;
ASSERT_GT(got, 0);
sz += got;
}
while (sz < snd.size());
ASSERT_EQ(sz, snd.size());
ASSERT_EQ(memcmp(buf, snd.data(), sz), 0);
log << "end" << endl;
}
void udp_writeread_test(InetUdpSock& sock, InetAddr& addr)
{
Logger log;
log.add_cout();
log.id("inet udp client");
log << "sending " << snd.size() << " to " << addr.str() << endl;
ASSERT_EQ(
sock.send(snd.data(), snd.size(), addr), snd.size());
char buf[snd.size()];
size_t got;
InetAddr from;
got =
sock.recv(buf, snd.size(), from);
log << "got reply " << got << " from " << from.host() << endl;
ASSERT_EQ(got, snd.size());
ASSERT_EQ(memcmp(buf, snd.data(), snd.size()), 0);
log << "end" << endl;
}
static int udp_readwrite_server(Event& startev, InetUdpSock& sock, InetAddr& addr, InetAddr& from, Event& quitev)
{
Logger log;
log.add_cout();
log.id("inet udp server");
try
{
log << "reuse_addr" << endl;
log << "bind to " << addr.host() << endl;
startev.write(1);
Poller pin;
pin.set(
sock.fd(), Poller::input);
pin.set(quitev.fd(), Poller::input);
while (1)
{
pin.wait();
if (pin.event(quitev.fd()))
{
startev.write(1);
return 0;
}
else if (pin.event(
sock.fd()))
{
auto sz =
sock.recv_next();
char buf[sz];
sock.recv(buf, sz, from);
log << "got " << sz << " from " << from.host() << endl;
sock.send(buf, sz, from);
}
}
}
catch (const std::exception& e)
{
log << "exception: " << e.what() << endl;
startev.write(1);
return errno;
}
startev.write(1);
return 0;
}
static int tcp_iostream_server(Event& startev, InetTcpSock& sock, InetAddr& addr, InetAddr& from)
{
Logger log;
log.add_cout();
log.id("inet tcp stream server");
try
{
log << "reuse_addr" << endl;
log << "bind to " << addr.host() << endl;
log << "listen" << endl;
startev.write(1);
auto conn =
sock.accept(from);
log << "connect from " << from << endl;
IoStream stream(conn, conn);
for (string got; std::getline(stream, got);)
{
log << "got " << got.size() << endl;
stream << got << endl;
}
}
catch (const std::exception& e)
{
log << "exception: " << e.what() << endl;
startev.write(1);
return errno;
}
startev.write(1);
return 0;
}
static int tcp_readwrite_server(Event& startev, InetTcpSock& sock, InetAddr& addr, InetAddr& from)
{
Logger log;
log.add_cout();
log.id("inet tcp readwrite server");
try
{
log << "reuse_addr" << endl;
log << "bind to " << addr.host() << endl;
log << "listen" << endl;
startev.write(1);
auto conn =
sock.accept(from);
log << "connect from " << from << endl;
char buf[16];
int sz;
while ((sz = conn.recv(buf, 16)) > 0)
{
log << "got " << sz << endl;
conn.send(buf, sz);
}
}
catch (const std::exception& e)
{
log << "exception: " << e.what() << endl;
startev.write(1);
return errno;
}
startev.write(1);
return 0;
}
};
TEST_F(inet_networking, udp_readwrite_loopback)
{
server_addr.host("::1");
server_addr.port(9999);
client_addr.host("::1");
client_addr.port(9999);
udp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
reset();
server_addr.host("::ffff:127.0.0.1");
server_addr.port(9999);
client_addr.host("::ffff:127.0.0.1");
client_addr.port(9999);
udp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
}
TEST_F(inet_networking, udp_readwrite_unicast)
{
server_addr.port(9999);
cout << "ipv4 server addr: " << server_addr << endl;
client_addr.host(server_addr.host());
client_addr.port(9999);
udp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
reset();
if (!inum)
{
cout << "NOTE: no interface local address found, skipping" << endl;
GTEST_SKIP();
}
cout << "ipv6 link local server addr: " << server_addr << endl;
server_addr.port(9999);
client_addr.host(server_addr.host());
client_addr.port(9999);
udp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
reset();
if (!inum)
{
cout << "NOTE: no interface local address found, skipping" << endl;
GTEST_SKIP();
}
cout << "ipv6 global server addr: " << server_addr << endl;
server_addr.port(9999);
client_addr.host(server_addr.host());
client_addr.port(9999);
udp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
}
TEST_F(inet_networking, tcp_iostream_loopback)
{
server_addr.host("::1");
server_addr.port(9999);
tcp_iostream_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
reset();
server_addr.host("::ffff:127.0.0.1");
server_addr.port(9999);
tcp_iostream_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
}
TEST_F(inet_networking, tcp_readwrite_loopback)
{
server_addr.host("::1");
server_addr.port(9999);
tcp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
reset();
server_addr.host("::ffff:127.0.0.1");
server_addr.port(9999);
tcp_readwrite_client();
log << "server connect from " << servfrom << endl;
ASSERT_EQ(server_addr.host(), servfrom.host());
}
TEST(inet_example, client_server_stream_test)
{
Logger log;
log.add_cout();
log.id("tcp client");
std::packaged_task<int(void)> serv_task([&addr, &listening]()
{
Logger log;
log.add_cout();
log.id("tcp server");
try
{
log << "server listening" << endl;
log << "signalling client" << endl;
auto conn =
sock.accept(from);
log << "connection from " << from << endl;
int count = 0;
for (string got; std::getline(stream, got);)
{
log << "got " << got << endl;
count += got.size();
stream << got << endl;
}
return count;
}
catch (exception& ex)
{
log << ex.what() << endl;
return -1;
}
});
auto fut = serv_task.get_future();
std::thread serv(std::move(serv_task));
Poller pin;
pin.set(listening, Poller::input);
while (1)
{
pin.wait(std::chrono::milliseconds(700));
if (pin.event(listening))
{
break;
}
log << "waiting for listener" << endl;
}
error_code ec;
if (ec.value() != 0 && ec.value() != EINPROGRESS)
{
log << "Non blocking connect failed: " << ec.message() << endl;
ASSERT_EQ(ec.value(), 0);
}
log << "waiting for 200 ms seconds to connect" << endl;
Poller pout;
pout.set(sock, Poller::output);
pout.wait(std::chrono::milliseconds(200));
if (!pout.event(sock))
{
log << "connect attempt timed out" << endl;
ASSERT_EQ(pout.event(sock), 0);
}
sock.non_blocking(
false);
log << "connected, sending stuff" << endl;
string line1("first line");
string line2("second line");
string got;
ASSERT_TRUE(stream << line1 << endl);
ASSERT_TRUE(getline(stream, got));
ASSERT_EQ(got, line1);
ASSERT_TRUE(stream << line2 << endl);
ASSERT_TRUE(getline(stream, got));
ASSERT_EQ(got, line2);
serv.join();
int res = fut.get();
log << "got result=" << res << endl;
ASSERT_EQ(res, line1.size()+line2.size());
}
TEST(inet_example, tcp_multiserver)
{
Logger log;
log.add_cout();
log.id("tcp client");
condition_variable cv;
mutex mx;
int listening = 0;
Event shut;
auto serv_task = [&addr, &mx, &listening, &cv, &shut](int i)
{
Logger log;
log.add_cout();
log.id(string("tcp server ")+to_string(i));
try
{
log << "server listening" << endl;
log << "telling the client I am listening" << endl;
{
lock_guard<mutex> lk(mx);
listening++;
cv.notify_one();
}
int count = 0;
for (;;)
{
Poller p;
p.set(sock, Poller::input);
p.set(shut, Poller::input);
p.wait();
if (p.event(shut))
{
return count;
}
InetAddr from;
auto conn =
sock.accept(from);
log << "connection from " << from << endl;
IoStream stream(conn, conn);
for (string got; std::getline(stream, got);)
{
log << "got size " << got.size() << " : " << got << endl;
count += got.size();
stream << got << endl;
}
}
return count;
}
catch (exception& ex)
{
log << ex.what() << endl;
lock_guard<mutex> lk(mx);
listening++;
cv.notify_one();
return 0;
}
};
auto fut1 = async(serv_task, 1);
auto fut2 = async(serv_task, 2);
auto fut3 = async(serv_task, 3);
unique_lock<mutex> lk(mx);
cv.wait(lk, [&listening]
{
return listening == 3;
});
int count = 0;
for (int i = 0; i < 10; i++)
{
log << "connected #" << i << ", sending stuff" << endl;
string line1("first line");
string line2("second line");
IoStream stream(sock, sock);
string got;
ASSERT_TRUE(stream << line1 << endl);
ASSERT_TRUE(getline(stream, got));
ASSERT_EQ(got, line1);
ASSERT_TRUE(stream << line2 << endl);
ASSERT_TRUE(getline(stream, got));
ASSERT_EQ(got, line2);
count += line1.size()+line2.size();
}
shut.write(1);
int res = fut1.get();
res += fut2.get();
res += fut3.get();
log << "got result=" << res << endl;
ASSERT_EQ(res, count);
}
unsigned port() const
Get the port.
virtual std::string host() const
Get host.
Internet transmission control protocol (tcp) socket.
Internet user datagram protocol (udp) socket.
static std::vector< NetIf > all_interfaces()
List network interfaces on the local system.
Signaling kernel event counter.
uint64_t read()
Read from (decrement) the event counter.
void reset(int=-1)
Reset the event.
void write(uint64_t)
Write to (increment) the event counter.
Input/output stream wrapper for reader/writer.
Thread-safe stream logger.
void add_cout()
Add std::cout console stream.
Poller which allows polling of generic file descriptors for various events.
Signaling kernel event counter.
NetIfFlag
Interface flags.
@ if_up
Interface is running.
InetAddrFlag
Internet address flags.
@ unicast
Unicast address.
@ link_local
Traffic is restricted to the local link.
@ global
Global traffic is allowed.
Internet tcp and udp networking.
Base input/output stream classes.
Internet network interface utility.
Linux kernel i/o event notification (poller).
TEST(inet_example, client_server_stream_test)
[Inet client server]
Unix domain tcp and udp networking.