#include <gtest/gtest.h>
#include <iostream>
#include <system_error>
#include <cerrno>
#include <thread>
#include <future>
#include <sstream>
using std::cout;
using std::endl;
using std::string;
using std::async;
using std::stringstream;
std::string server_addrname("/tmp/server.sock");
std::string client_addrname("/tmp/client.sock");
struct unix_networking : public testing::Test
{
std::string snd, got, testgot;
UnixAddr server_addr, client_addr;
unix_networking() : snd("this is a test line\nthis is the second\nanother\n\nlast one")
{
Logger log;
log.id("test");
std::system_error err;
server_addr.host(server_addrname);
client_addr.host(client_addrname);
}
virtual ~unix_networking()
{
std::system_error err;
}
void tcp_stream_test(UnixTcpSock& sock)
{
Logger log;
log.add_cout();
log.id("unix 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 << "tcp_stream_test: end" << endl;
}
void tcp_writeread_test(UnixTcpSock& sock)
{
Logger log;
log.add_cout();
log.id("unix 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(UnixUdpSock& sock, UnixAddr& addr)
{
Logger log;
log.add_cout();
log.id("unix 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;
UnixAddr 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, UnixUdpSock& sock, UnixAddr& addr, UnixAddr& from, Event& quitev)
{
Logger log;
log.add_cout();
log.id("unix 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, UnixTcpSock& sock, UnixAddr& addr, UnixAddr& from)
{
Logger log;
log.add_cout();
log.id("unix 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, UnixTcpSock& sock, UnixAddr& addr, UnixAddr& from)
{
Logger log;
log.add_cout();
log.id("unix 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(unix_networking, addrnames)
{
ASSERT_EQ(server_addr.host(), server_addrname);
ASSERT_EQ(client_addr.host(), client_addrname);
}
TEST_F(unix_networking, udp_readwrite)
{
UnixUdpSock servsock;
UnixAddr from;
auto fut = async(udp_readwrite_server, std::ref(startev), std::ref(servsock), std::ref(server_addr), std::ref(from), std::ref(quitev));
startev.read();
log << "binding to " << client_addr << endl;
udp_writeread_test(sock, server_addr);
quitev.write(1);
int res = fut.get();
log << "return code: " << res << endl;
ASSERT_EQ(res, 0);
ASSERT_EQ(from.host(), client_addr.host());
}
TEST_F(unix_networking, tcp_iostream)
{
UnixTcpSock servsock;
UnixAddr from;
auto fut = async(tcp_iostream_server, std::ref(startev), std::ref(servsock), std::ref(server_addr), std::ref(from));
startev.read();
log << "binding to " << client_addr << endl;
log << "connect" << endl;
sock.connect(server_addr);
log << "connected to addr name=" << server_addr.host() << endl;
tcp_stream_test(sock);
int res = fut.get();
log << "return code: " << res << endl;
ASSERT_EQ(res, 0);
ASSERT_EQ(from.host(), client_addr.host());
}
TEST_F(unix_networking, tcp_readwrite)
{
UnixTcpSock servsock;
UnixAddr from;
auto fut = async(tcp_readwrite_server, std::ref(startev), std::ref(servsock), std::ref(server_addr), std::ref(from));
startev.read();
log << "binding to " << client_addr << endl;
log << "connect" << endl;
sock.connect(server_addr);
log << "connected to addr name=" << server_addr.host() << endl;
tcp_writeread_test(sock);
int res = fut.get();
log << "return code: " << res << endl;
ASSERT_EQ(res, 0);
ASSERT_EQ(from.host(), client_addr.host());
}
A unix domain address, which is a file of type "socket.".
Unix domain tcp (stream) socket.
Unix domain udp (datagram) socket.
Signaling kernel event counter.
static void remove(const std::string &, std::system_error *=nullptr)
Remove the file or directory.
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.
Common file system utilities.
Internet tcp and udp networking.
Base input/output stream classes.
Linux kernel i/o event notification (poller).
Unix domain tcp and udp networking.