Mercurial > code
view C++/Tests/Sockets/main.cpp @ 280:91eb0583df52
Socket:
* Fix select method that require fdset's to be rebuild at each iteration,
* While here always create a m_interface
Task: #306
author | David Demelier <markand@malikania.fr> |
---|---|
date | Fri, 24 Oct 2014 18:28:39 +0200 |
parents | adcae2bde2f0 |
children | ea55a3886da0 |
line wrap: on
line source
/* * main.cpp -- test sockets * * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <chrono> #include <iostream> #include <sstream> #include <string> #include <thread> #include <gtest/gtest.h> #include <Socket.h> #include <SocketListener.h> #include <SocketAddress.h> using namespace std::literals::chrono_literals; using namespace error; using namespace address; /* -------------------------------------------------------- * Miscellaneous * -------------------------------------------------------- */ TEST(Misc, set) { Socket s; try { s = { AF_INET6, SOCK_STREAM, 0 }; s.set(IPPROTO_IPV6, IPV6_V6ONLY, 0); ASSERT_EQ(0, s.get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); s.set(IPPROTO_IPV6, IPV6_V6ONLY, 1); ASSERT_EQ(1, s.get<int>(IPPROTO_IPV6, IPV6_V6ONLY)); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); } TEST(Misc, initializer) { Socket s1, s2; try { s1 = { AF_INET6, SOCK_STREAM, 0 }; s2 = { AF_INET6, SOCK_STREAM, 0 }; SocketListener listener { { s1, Read }, { s2, Read }, }; ASSERT_EQ(2UL, listener.size()); listener.list([&] (const auto &so, auto direction) { ASSERT_TRUE(so == s1 || so == s2); ASSERT_EQ(Read, direction); }); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s1.close(); s2.close(); } /* -------------------------------------------------------- * Select tests * -------------------------------------------------------- */ TEST(ListenerMethodSelect, timeout) { std::thread server([] () { Socket s, client; SocketListener listener(Select); bool running = true; int tries = 0; try { s = { AF_INET, SOCK_STREAM, 0 }; s.set(SOL_SOCKET, SO_REUSEADDR, 1); s.bind(Internet{"*", 10000, AF_INET}); s.listen(10); listener.add(s, Read); while (running) { try { listener.select(500ms); client = s.accept(); running = false; // Abort if no client connected if (tries >= 10) running = false; } catch (const Timeout &) { puts("DEBUG: TIMEOUT"); } } } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); client.close(); }); std::thread client([] () { std::this_thread::sleep_for(2s); Socket s; try { s = { AF_INET, SOCK_STREAM, 0 }; s.connect(Internet{"localhost", 10000, AF_INET}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } TEST(ListenerMethodSelect, add) { Socket s, s2; try { s = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Select); listener.add(s, Read); listener.add(s2, Read); ASSERT_EQ(2UL, listener.size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); s2.close(); } TEST(ListenerMethodSelect, remove) { Socket s, s2; try { s = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Select); listener.add(s, Read); listener.add(s2, Read); listener.remove(s, Read); listener.remove(s2, Read); ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); s2.close(); } /* * Add two sockets for both reading and writing, them remove only reading and then * move only writing. */ TEST(ListenerMethodSelect, inOut) { Socket s, s2; try { s = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Select); listener.add(s, Read | Write); listener.add(s2, Read | Write); listener.list([&] (Socket &si, int dir) { ASSERT_TRUE(si == s || si == s2); ASSERT_EQ(0x03, static_cast<int>(dir)); }); listener.remove(s, Write); listener.remove(s2, Write); ASSERT_EQ(2UL, listener.size()); listener.list([&] (Socket &si, int dir) { ASSERT_TRUE(si == s || si == s2); ASSERT_EQ(Read, dir); }); listener.remove(s, Read); listener.remove(s2, Read); ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); s2.close(); } TEST(ListenerMethodSelect, addSame) { Socket s; try { s = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Select); listener.add(s, Read); ASSERT_EQ(1UL, listener.size()); listener.list([&] (const Socket &si, int dir) { ASSERT_TRUE(si == s); ASSERT_EQ(Read, dir); }); listener.add(s, Write); ASSERT_EQ(1UL, listener.size()); listener.list([&] (const Socket &si, int dir) { ASSERT_TRUE(si == s); ASSERT_EQ(0x03, static_cast<int>(dir)); }); // Oops, added the same listener.add(s, Read); listener.add(s, Write); listener.add(s, Read | Write); ASSERT_EQ(1UL, listener.size()); listener.list([&] (const Socket &si, int dir) { ASSERT_TRUE(si == s); ASSERT_EQ(0x03, static_cast<int>(dir)); }); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); } /* -------------------------------------------------------- * Poll tests * -------------------------------------------------------- */ TEST(ListenerMethodPoll, timeout) { std::thread server([] () { Socket s, client; SocketListener listener(Poll); bool running = true; int tries = 0; try { s = { AF_INET, SOCK_STREAM, 0 }; s.set(SOL_SOCKET, SO_REUSEADDR, 1); s.bind(Internet{"*", 10000, AF_INET}); s.listen(10); listener.add(s, Read); while (running) { try { listener.select(500ms); client = s.accept(); running = false; // Abort if no client connected if (tries >= 10) running = false; } catch (const Timeout &) { puts("DEBUG: TIMEOUT"); } } } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); client.close(); }); std::thread client([] () { std::this_thread::sleep_for(2s); Socket s; try { s = { AF_INET, SOCK_STREAM, 0 }; s.connect(Internet{"localhost", 10000, AF_INET}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } TEST(ListenerMethodPoll, add) { Socket s, s2; try { s = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Poll); listener.add(s, Read); listener.add(s2, Read); ASSERT_EQ(2UL, listener.size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); s2.close(); } TEST(ListenerMethodPoll, remove) { Socket s, s2; try { s = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Poll); listener.add(s, Read); listener.add(s2, Read); listener.remove(s, Read); listener.remove(s2, Read); ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); s2.close(); } TEST(ListenerMethodPoll, inOut) { Socket s, s2; try { s = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Poll); listener.add(s, Read | Write); listener.add(s2, Read | Write); listener.list([&] (Socket &si, int dir) { ASSERT_TRUE(si == s || si == s2); ASSERT_EQ(0x03, static_cast<int>(dir)); }); listener.remove(s, Write); listener.remove(s2, Write); ASSERT_EQ(2UL, listener.size()); listener.list([&] (Socket &si, int dir) { ASSERT_TRUE(si == s || si == s2); ASSERT_EQ(Read, dir); }); listener.remove(s, Read); listener.remove(s2, Read); ASSERT_EQ(0UL, listener.size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); s2.close(); } TEST(ListenerMethodPoll, addSame) { Socket s; try { s = { AF_INET, SOCK_STREAM, 0 }; SocketListener listener(Poll); listener.add(s, Read); ASSERT_EQ(1UL, listener.size()); listener.list([&] (const Socket &si, int dir) { ASSERT_TRUE(si == s); ASSERT_EQ(Read, dir); }); listener.add(s, Write); ASSERT_EQ(1UL, listener.size()); listener.list([&] (const Socket &si, int dir) { ASSERT_TRUE(si == s); ASSERT_EQ(0x03, static_cast<int>(dir)); }); // Oops, added the same listener.add(s, Read); listener.add(s, Write); listener.add(s, Read | Write); ASSERT_EQ(1UL, listener.size()); listener.list([&] (const Socket &si, int dir) { ASSERT_TRUE(si == s); ASSERT_EQ(0x03, static_cast<int>(dir)); }); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); } /* -------------------------------------------------------- * Socket listener class * -------------------------------------------------------- */ TEST(Listener, connection) { std::thread server([] () { Socket s; SocketListener listener; try { s = { AF_INET, SOCK_STREAM, 0 }; s.set(SOL_SOCKET, SO_REUSEADDR, 1); s.bind(Internet{"*", 10000, AF_INET}); s.listen(8); listener.add(s, Read); auto client = listener.select(10s); ASSERT_TRUE(client.direction == Read); ASSERT_TRUE(client.socket == s); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); std::thread client([] () { Socket client; std::this_thread::sleep_for(500ms); try { client = { AF_INET, SOCK_STREAM, 0 }; client.connect(Internet{"localhost", 10000, AF_INET}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } client.close(); }); server.join(); client.join(); } TEST(Listener, connectionAndRead) { std::thread server([] () { Socket s; SocketListener listener; try { s = { AF_INET, SOCK_STREAM, 0 }; s.set(SOL_SOCKET, SO_REUSEADDR, 1); s.bind(Internet("*", 10000, AF_INET)); s.listen(8); // Read for master listener.add(s, Read); auto result = listener.select(10s); ASSERT_TRUE(result.direction == Read); ASSERT_TRUE(result.socket == s); // Wait for client auto client = s.accept(); listener.add(client, Read); result = listener.select(10s); ASSERT_TRUE(result.direction == Read); ASSERT_TRUE(result.socket == client); char data[512]; auto nb = client.recv(data, sizeof (data) - 1); data[nb] = '\0'; client.close(); ASSERT_STREQ("hello world", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); std::thread client([] () { Socket client; std::this_thread::sleep_for(500ms); try { client = Socket(AF_INET, SOCK_STREAM, 0); client.connect(Internet("localhost", 10000, AF_INET)); client.send("hello world"); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } client.close(); }); server.join(); client.join(); } TEST(Listener, bigData) { std::thread server([] () { std::ostringstream out; Socket server; SocketListener listener; bool finished(false); try { server = { AF_INET, SOCK_STREAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.bind(Internet{"*", 10000, AF_INET}); server.listen(10); listener.add(server, Read); while (!finished) { auto s = listener.select(60s).socket; if (s == server) { listener.add(s.accept(), Read); } else { char data[512]; auto nb = s.recv(data, sizeof (data) - 1); if (nb == 0) finished = true; else { data[nb] = '\0'; out << data; } } } ASSERT_EQ(9000002UL, out.str().size()); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); }); std::thread client([] () { std::string data; data.reserve(9000000); for (int i = 0; i < 9000000; ++i) data.push_back('a'); data.push_back('\r'); data.push_back('\n'); std::this_thread::sleep_for(500ms); Socket client; SocketListener listener; try { client = { AF_INET, SOCK_STREAM, 0 }; client.connect(Internet{"localhost", 10000, AF_INET}); client.blockMode(false); listener.add(client, Write); while (data.size() > 0) { auto s = listener.select(30s).socket; auto nb = s.send(data.data(), data.size()); data.erase(0, nb); } } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } client.close(); }); server.join(); client.join(); } /* -------------------------------------------------------- * Multiple selection tests * -------------------------------------------------------- */ TEST(MultipleSelection, select) { /* * Normally, 3 sockets added for writing should be marked ready immediately * as there are no data being currently queued to be sent. */ std::thread server([] () { Socket master; try { SocketListener masterListener, clientListener; master = { AF_INET, SOCK_STREAM, 0 }; master.set(SOL_SOCKET, SO_REUSEADDR, 1); master.bind(Internet{"*", 10000, AF_INET}); master.listen(8); masterListener.add(master, Read); while (clientListener.size() != 3) { masterListener.select(3s); clientListener.add(master.accept(), Write); } // Now do the test of writing auto result = clientListener.selectMultiple(3s); ASSERT_EQ(3UL, result.size()); clientListener.list([] (auto s, auto) { s.close(); }); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } master.close(); }); std::thread client([] () { Socket s1, s2, s3; try { s1 = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; s3 = { AF_INET, SOCK_STREAM, 0 }; std::this_thread::sleep_for(1s); s1.connect(Internet{"localhost", 10000, AF_INET}); s2.connect(Internet{"localhost", 10000, AF_INET}); s3.connect(Internet{"localhost", 10000, AF_INET}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s1.close(); s2.close(); s3.close(); }); server.join(); client.join(); } #if defined(SOCKET_LISTENER_HAVE_POLL) TEST(MultipleSelection, poll) { /* * Normally, 3 sockets added for writing should be marked ready immediately * as there are no data being currently queued to be sent. */ std::thread server([] () { Socket master; try { SocketListener masterListener(Poll), clientListener(Poll); master = { AF_INET, SOCK_STREAM, 0 }; master.set(SOL_SOCKET, SO_REUSEADDR, 1); master.bind(Internet{"*", 10000, AF_INET}); master.listen(8); masterListener.add(master, Read); while (clientListener.size() != 3) { masterListener.select(3s); clientListener.add(master.accept(), Write); } // Now do the test of writing auto result = clientListener.selectMultiple(3s); ASSERT_EQ(3UL, result.size()); clientListener.list([] (auto s, auto) { s.close(); }); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } master.close(); }); std::thread client([] () { Socket s1, s2, s3; try { s1 = { AF_INET, SOCK_STREAM, 0 }; s2 = { AF_INET, SOCK_STREAM, 0 }; s3 = { AF_INET, SOCK_STREAM, 0 }; std::this_thread::sleep_for(1s); s1.connect(Internet("localhost", 10000, AF_INET)); s2.connect(Internet("localhost", 10000, AF_INET)); s3.connect(Internet("localhost", 10000, AF_INET)); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s1.close(); s2.close(); s3.close(); }); server.join(); client.join(); } #endif /* -------------------------------------------------------- * Basic TCP tests * -------------------------------------------------------- */ TEST(BasicTcp, sendipv4) { std::thread server([] () { Socket server, client; try { server = { AF_INET, SOCK_STREAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.bind(Internet{"*", 10000, AF_INET}); server.listen(8); client = server.accept(); char data[512]; auto nb = client.recv(data, sizeof (data) - 1); data[nb] = '\0'; ASSERT_STREQ("hello", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); client.close(); }); std::thread client([] () { Socket s; std::this_thread::sleep_for(500ms); try { s = { AF_INET, SOCK_STREAM, 0 }; s.connect(Internet{"localhost", 10000, AF_INET}); s.send("hello"); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } TEST(BasicTcp, sendipv6) { std::thread server([] () { Socket server, client; try { server = { AF_INET6, SOCK_STREAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.bind(Internet("*", 10000, AF_INET6)); server.listen(8); client = server.accept(); char data[512]; auto nb = client.recv(data, sizeof (data) - 1); data[nb] = '\0'; ASSERT_STREQ("hello", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); client.close(); }); std::thread client([] () { Socket s; std::this_thread::sleep_for(500ms); try { s = { AF_INET6, SOCK_STREAM, 0 }; s.connect(Internet{"ip6-localhost", 10000, AF_INET6}); s.send("hello"); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } #if !defined(_WIN32) TEST(BasicTcp, sendunix) { std::thread server([] () { Socket server, client; try { server = { AF_UNIX, SOCK_STREAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.bind(Unix("/tmp/gtest-send-tcp-unix.sock", true)); server.listen(8); client = server.accept(); char data[512]; auto nb = client.recv(data, sizeof (data) - 1); data[nb] = '\0'; ASSERT_STREQ("hello", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); client.close(); }); std::thread client([] () { Socket s; std::this_thread::sleep_for(500ms); try { s = { AF_UNIX, SOCK_STREAM, 0 }; s.connect(Unix{"/tmp/gtest-send-tcp-unix.sock"}); s.send("hello"); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } #endif /* -------------------------------------------------------- * Basic UDP tests * -------------------------------------------------------- */ TEST(BasicUdp, sendipv4) { std::thread server([] () { Socket server; try { server = { AF_INET, SOCK_DGRAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.bind(Internet("*", 10000, AF_INET)); char data[512]; auto nb = server.recvfrom(data, sizeof (data) - 1); data[nb] = '\0'; ASSERT_STREQ("hello", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); }); std::thread client([] () { Socket s; std::this_thread::sleep_for(500ms); try { s = { AF_INET, SOCK_DGRAM, 0 }; s.sendto("hello", Internet{"localhost", 10000, AF_INET}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } TEST(BasicUdp, sendipv6) { std::thread server([] () { Socket server; try { server = { AF_INET6, SOCK_DGRAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.set(IPPROTO_IPV6, IPV6_V6ONLY, 1); server.bind(Internet{"*", 10000, AF_INET6}); char data[512]; auto nb = server.recvfrom(data, sizeof (data) - 1); data[nb] = '\0'; ASSERT_STREQ("hello", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); }); std::thread client([] () { Socket s; std::this_thread::sleep_for(500ms); try { s = { AF_INET6, SOCK_DGRAM, 0 }; s.sendto("hello", Internet{"ip6-localhost", 10000, AF_INET6}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } #if !defined(_WIN32) TEST(BasicUdp, sendunix) { std::thread server([] () { Socket server; try { server = { AF_UNIX, SOCK_DGRAM, 0 }; server.set(SOL_SOCKET, SO_REUSEADDR, 1); server.bind(Unix{"/tmp/gtest-send-udp-unix.sock", true}); char data[512]; auto nb = server.recvfrom(data, sizeof (data) - 1); data[nb] = '\0'; ASSERT_STREQ("hello", data); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } server.close(); }); std::thread client([] () { Socket s; std::this_thread::sleep_for(500ms); try { s = { AF_UNIX, SOCK_DGRAM, 0 }; s.sendto("hello", Unix{"/tmp/gtest-send-udp-unix.sock"}); } catch (const std::exception &ex) { std::cerr << "warning: " << ex.what() << std::endl; } s.close(); }); server.join(); client.join(); } #endif int main(int argc, char **argv) { Socket::init(); testing::InitGoogleTest(&argc, argv); auto ret = RUN_ALL_TESTS(); Socket::finish(); return ret; }